dancasdev/schema-validator

Schema-based data validator. Clean, flexible, framework-agnostic.

Maintainers

Package info

github.com/DancasDev/SchemaValidator

pkg:composer/dancasdev/schema-validator

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-04 19:47 UTC

This package is auto-updated.

Last update: 2026-08-04 20:04:59 UTC


README

Librería PHP liviana (sin dependencias externas) que valida arreglos de datos contra un esquema declarativo. Responde una sola pregunta: "¿este arreglo de datos cumple con la estructura que espero?" — y te devuelve si es válido, los errores (si los hay) y solamente los datos que declaraste en el esquema.

Instalación

composer require dancasdev/schema-validator

PHP 8.1 o superior. No requiere extensiones adicionales.

Uso básico

1. Definir un esquema

use DancasDev\SchemaValidator\SchemaValidator;

$esquema = [
    'nombre' => [
        'type'    => 'string',
        'rules'   => ['required' => true, 'min_length' => 2],
    ],
    'edad' => [
        'type'    => 'int',
        'rules'   => ['required' => true, 'min' => 18, 'max' => 99],
    ],
];

Todo campo del esquema comparte estas propiedades:

Propiedad Obligatorio Descripción
type Tipo del campo. Ver Tipos de datos
rules No Reglas de validación. Dependen del tipo
default No Valor por defecto si el campo no está presente
messages No Mensajes de error personalizados para este campo

Algunos tipos añaden claves propias del campo: object usa schema y array usa item_schema. Se documentan en su sección correspondiente, ya que no son globales.

2. Validar datos

$validador = new SchemaValidator();

$datos = [
    'nombre' => 'Juan',
    'edad'   => 25,
];

$resultado = $validador->validate($datos, $esquema);

if ($resultado->isValid()) {
    echo "¡Todo correcto!";
}

3. Leer el resultado

Método Descripción
isValid() true si no hay errores
errors() ['ruta' => 'mensaje', ...] — todos los errores
error('edad') Mensaje de un campo específico, o null si no tiene
hasError('edad') true si ese campo tiene error
data() Arreglo con los datos resueltos (solo campos válidos)
get('a.b') Valor de un campo por ruta con notación de puntos
toArray() ['valid' => bool, 'errors' => [...], 'data' => [...]]
$resultado->errors();         // ['ruta' => 'mensaje', ...]
$resultado->error('edad');    // mensaje de un campo específico
$resultado->hasError('edad'); // true si ese campo tiene error
$resultado->data();           // arreglo con los datos limpios
$resultado->get('nombre');    // valor de un campo específico

4. Ejemplo completo

use DancasDev\SchemaValidator\SchemaValidator;

$esquema = [
    'usuario' => [
        'type'  => 'string',
        'rules' => ['required' => true, 'min_length' => 3, 'max_length' => 20],
    ],
    'email' => [
        'type'  => 'string',
        'rules' => ['required' => true, 'pattern' => '/^[^@]+@[^@]+\.[^@]+$/'],
    ],
    'edad' => [
        'type'  => 'int',
        'rules' => ['required' => true, 'min' => 13],
    ],
    'roles' => [
        'type'        => 'array',
        'rules'       => ['item_type' => 'object'],
        'item_schema' => [
            'nombre' => ['type' => 'string', 'rules' => ['required' => true]],
        ],
    ],
];

$validador = new SchemaValidator([], [
    'required' => 'El campo es obligatorio.',
    'string'   => 'Debe ser texto.',
]);

$resultado = $validador->validate($datos, $esquema);

if (!$resultado->isValid()) {
    foreach ($resultado->errors() as $ruta => $mensaje) {
        echo "{$ruta}: {$mensaje}\n";
    }
}

$datosLimpios = $resultado->data();

Tipos de datos

Cada tipo define sus propias reglas (dentro de rules). Algunos añaden además claves extra a nivel del campo (junto a type/rules).

string

'correo' => [
    'type'  => 'string',
    'rules' => [
        'min_length' => 5,
        'max_length' => 100,
        'enum'       => ['admin', 'user', 'guest'],  // valores permitidos
        'pattern'    => '/^[^@]+@[^@]+\.[^@]+$/',   // expresión regular
    ],
]
Regla Tipo Descripción
required bool Campo obligatorio
min_length int Longitud mínima
max_length int Longitud máxima
enum array Valores permitidos (comparación estricta)
pattern string Expresión regular, evaluada con preg_match

min_length/max_length cuentan caracteres (multibyte si ext-mbstring está cargado; bytes en caso contrario). No se requieren extensiones.

int

'edad' => [
    'type'  => 'int',
    'rules' => ['required' => true, 'min' => 0, 'max' => 150],
]
Regla Tipo Descripción
required bool Campo obligatorio
min int Valor mínimo
max int Valor máximo

float

'precio' => [
    'type'  => 'float',
    'rules' => ['min' => 0.01, 'max' => 9999.99],
]
Regla Tipo Descripción
required bool Campo obligatorio
min float Valor mínimo
max float Valor máximo

number (acepta int y float)

'total' => [
    'type'  => 'number',
    'rules' => ['min' => 0, 'max' => 100000],
]
Regla Tipo Descripción
required bool Campo obligatorio
min number Valor mínimo
max number Valor máximo

bool

'activo' => [
    'type'  => 'bool',
    'rules' => ['required' => true],
]
Regla Tipo Descripción
required bool Campo obligatorio

array (lista)

array = lista secuencial. Los índices deben ser 0, 1, 2, .... Un array asociativo NO es array — use el tipo object.

'etiquetas' => [
    'type'  => 'array',
    'rules' => [
        'min_length'  => 1,
        'max_length'  => 10,
        'item_type'   => 'string',  // tipo de cada elemento
        'set'         => ['a', 'b', 'c'],  // valores permitidos
    ],
]
Regla Tipo Descripción
required bool Campo obligatorio
min_length int Cantidad mínima de elementos
max_length int Cantidad máxima de elementos
item_type string Tipo de cada elemento
set array Valores permitidos

Valores de item_type: string, int, float, number, bool, array (cada elemento es una lista) y object (cada elemento es cualquier array).

Clave extra Tipo Descripción
item_schema array Esquema para cada elemento. Requiere item_type => 'object'

Array de objetos (item_schema)

'empleados' => [
    'type'        => 'array',
    'rules'       => ['item_type' => 'object'],
    'item_schema' => [
        'nombre' => ['type' => 'string', 'rules' => ['required' => true]],
        'edad'   => ['type' => 'int',    'rules' => ['min' => 0]],
    ],
]

Cada elemento se valida contra item_schema. Los errores usan la ruta con el índice del elemento: empleados.0.nombre, empleados.1.edad, etc.

object (cualquier array)

object = cualquier array (asociativo o lista). La distinción con array es que array solo acepta listas.

'meta' => [
    'type'   => 'object',
    'rules'  => ['required' => true],
    'schema' => [
        'clave' => ['type' => 'string', 'rules' => ['required' => true]],
        'valor' => ['type' => 'string', 'rules' => []],
    ],
]
Regla Tipo Descripción
required bool Campo obligatorio
Clave extra Tipo Descripción
schema array Sub-esquema de las propiedades del objeto

Objetos anidados

Con schema, cada propiedad se valida recursivamente y los errores se registran con ruta punteada (meta.clave, meta.otra.prop).

Tipos múltiples (type como array)

Puedes especificar varios tipos aceptables. El validador prueba cada uno y si al menos uno pasa, el campo es válido. Las reglas se evalúan solo para el tipo que coincidió — reglas que no aplican se ignoran.

$esquema = [
    'id' => [
        'type'  => ['string', 'int'],   // acepta string o int
        'rules' => [
            'min_length' => 1,           // solo aplica si es string
            'min'        => 0,           // solo aplica si es int
        ],
    ],
];
Valor ¿String? ¿Int? Resultado
"abc" ✅ pasa ❌ no es int ✅ válido
42 ❌ no es string ✅ pasa (min=0) ✅ válido
-1 ❌ no es string ❌ falla min ❌ inválido
3.14 ❌ no es string ❌ no es int ❌ inválido

NULL como tipo permitido

Para campos que acepten explícitamente null, incluye 'NULL' en el array de tipos:

$esquema = [
    'descuento' => [
        'type'  => ['number', 'NULL'],   // number o null
        'rules' => ['min' => 0, 'max' => 100],
    ],
];
Valor Resultado
15 ✅ válido
null ✅ válido (NULL en types)
"texto" ❌ inválido

Anidamiento en multi-type

object y array con schema/item_schema también funcionan en multi-type: el anidamiento se aplica cuando el valor es un array. Un valor que no es array cae a los demás tipos de la lista.

$esquema = [
    'meta' => [
        'type'   => ['object', 'NULL'],   // objeto o null
        'rules'  => [],
        'schema' => [
            'clave' => ['type' => 'string', 'rules' => ['required' => true]],
        ],
    ],
];
Valor Resultado
['clave' => 'x'] ✅ valida el sub-esquema
null ✅ válido (NULL en types)
['clave' => ''] ❌ inválido (hijo falla)

Comportamiento

Datos limpios

La librería solo devuelve los campos declarados en el esquema. Cualquier campo extra en los datos de entrada es ignorado:

$resultado = $validador->validate(
    ['nombre' => 'Juan', 'extra' => 'esto será ignorado'],
    ['nombre' => ['type' => 'string', 'rules' => []]],
);

$resultado->data(); // ['nombre' => 'Juan']  ← sin 'extra'

data() solo incluye campos que pasaron la validación. Un campo inválido (o un objeto/array con algún hijo inválido) queda fuera:

$resultado = $validador->validate(
    ['edad' => 'no-soy-numero'],
    ['edad' => ['type' => 'int', 'rules' => []]],
);

$resultado->data(); // []  ← 'edad' falló y no se incluye

Valores por defecto

Cuando un campo no está presente en los datos, puedes asignarle un valor por defecto:

$esquema = [
    'rol' => [
        'type'    => 'string',
        'rules'   => ['enum' => ['admin', 'user']],
        'default' => 'user',
    ],
];

$resultado = $validador->validate([], $esquema);
$resultado->data()['rol']; // 'user'

El valor por defecto también pasa por validación. Si no cumple las reglas, se reporta como error.

Campos vacíos y null

  • Un string vacío ('') en un campo no requerido se ignora: no genera error y no entra a data().
  • Un string vacío con required: true sí genera error.
  • null en un campo no requerido se ignora igual; con 'NULL' en type se valida explícitamente como permitido (ver Tipos múltiples).

Mensajes de error

Códigos y mensajes por defecto

Todos los errores se identifican por un código. Estos son los mensajes en inglés que se usan si no los personalizas:

Código Mensaje por defecto
required Is required.
object Must be an object.
string Must be a string.
string_min_length Must be at least {min} characters.
string_max_length Must be at most {max} characters.
string_enum Must be one of: {values}.
string_pattern Format is invalid.
int Must be an integer.
int_min Must be at least {min}.
int_max Must be at most {max}.
float Must be a float.
float_min Must be at least {min}.
float_max Must be at most {max}.
number Must be a number.
number_min Must be at least {min}.
number_max Must be at most {max}.
bool Must be a boolean.
array Must be an array.
array_item_type Must be of type {param}.
array_min_length Must have at least {min} items.
array_max_length Must have at most {max} items.
array_set Contains an invalid value.
type_mismatch Must be a {types}.
(código desconocido) Validation failed.

Personalización

Por instancia — aplica a todos los campos de ese validador:

$validador = new SchemaValidator([], [
    'required' => 'Este campo es obligatorio.',
    'string'   => 'Debe ser texto.',
    'int'      => 'Debe ser un número entero.',
]);

Por campo — gana sobre la instancia:

$esquema = [
    'correo' => [
        'type'     => 'string',
        'rules'    => ['required' => true],
        'messages' => [
            'required' => 'El correo es obligatorio.',
            'string'   => 'El correo debe ser texto.',
        ],
    ],
];

Jerarquía de resolución

1. Mensaje del campo (field.messages)
2. Mensaje de la instancia (constructor)
3. Mensaje hardcodeado en inglés (fallback)

Placeholders

Los mensajes pueden incluir placeholders que se reemplazan con los valores de las reglas:

Placeholder Se reemplaza por
{min} min_length o min
{max} max_length o max
{values} la lista de enum o set
{param} item_type
{types} la lista de tipos de type (multi-type)

Tipos de validación personalizados

Registra un validador propio con addType y úsalo como un tipo más:

use DancasDev\SchemaValidator\SchemaValidator;

$validador = new SchemaValidator();

$validador->addType('telefono', function (mixed $valor, array $reglas, string $ruta, callable $msg): ?string {
    if (!is_string($valor)) {
        return $msg('string');
    }
    if (!preg_match('/^\+?\d{7,15}$/', $valor)) {
        return 'Debe ser un teléfono válido.';
    }
    return null; // válido
});

$resultado = $validador->validate(
    ['tel' => '+521234567890'],
    ['tel' => ['type' => 'telefono', 'rules' => ['required' => true]]]
);

La firma del callable es fn(mixed $valor, array $reglas, string $ruta, callable $msg): ?string:

Parámetro Tipo Descripción
$valor mixed El valor del campo a validar
$reglas array Las reglas de rules filtradas para este tipo (solo las que le aplican)
$ruta string Ruta del campo (p. ej. usuario.nombre en anidados)
$msg callable fn(string $codigo): string — resuelve el mensaje del código siguiendo la jerarquía field > instancia > fallback

Valor de retorno:

  • null → el valor es válido.
  • string → mensaje de error.

Tests

composer test        # o: php test/run.php

Suite sin dependencias (asserts propios) que cubre todos los tipos, reglas, anidamiento, multi-type, mensajes y ValidationResult.

Licencia

MIT