ez-php / json-schema-validator
Validates decoded JSON against the JSON Schema subset ez-php/json-schema generates, with JSON Pointer error paths
Requires
- php: ^8.5
Requires (Dev)
- ez-php/contracts: ^2.6.0
- ez-php/docker: ^2.6.0
- ez-php/http: ^2.6.0
- ez-php/json-schema: ^2.6.0
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^13.0
Suggests
- ez-php/contracts: Needed for Http\JsonSchemaMiddleware (request body validation)
- ez-php/http: Needed for Http\JsonSchemaMiddleware (request body validation)
- ez-php/json-schema: Needed for Http\JsonSchemaMiddleware schemas generated from request classes
Provides
None
Conflicts
None
Replaces
None
README
Validates decoded JSON against JSON Schema, specifically the draft 2020-12 subset that ez-php/json-schema generates. Errors come with JSON Pointer paths. No runtime dependencies.
Installation
composer require ez-php/json-schema-validator
Usage
use EzPhp\JsonSchema\SchemaGenerator; use EzPhp\JsonSchemaValidator\Validator; $schema = (new SchemaGenerator())->generate(CreateCustomer::class); $result = (new Validator())->validate(json_decode($rawBody, true), $schema); if (!$result->isValid()) { return new Response(json_encode(['errors' => $result->messages()]), 422); // {"errors": {"/email": ["Value does not match format \"email\"."], "/address/city": ["Property \"city\" is required."]}} }
$result->errors() returns ValidationError objects (pointer, keyword, message). All errors are collected, not just the first.
Hand-written schemas work too, as long as they stay within the supported keywords.
Supported keywords
| Keyword | Notes |
|---|---|
type |
single type or list (["string", "null"]); 3.0 counts as integer |
enum |
JSON equality (1 equals 1.0) |
properties, required |
undeclared properties are allowed; a required error points at the missing property |
items |
one schema for every element |
anyOf |
reports one error at its own location |
format |
asserts date-time, date, email, uri, uuid; other formats are ignored |
pattern |
unanchored, Unicode |
minimum, maximum |
inclusive |
$ref |
local JSON Pointers into the root (#, #/$defs/X, #/components/schemas/X) |
true / false |
boolean schemas |
Annotations (description, default, title, examples, $schema, $defs, ...) are ignored. Any other keyword throws SchemaException. An unsupported constraint never passes silently.
Data
Pass data decoded with json_decode($json, true) (arrays) or as stdClass objects. With arrays, a list is a JSON array and any other array an object. An empty array satisfies both array and object, since json_decode can't tell [] from {}; decode to stdClass if that distinction matters.
Validating request bodies
Http\JsonSchemaMiddleware enforces a schema before the route handler runs. Register it as a middleware alias and pass the request class whose #[Property]-annotated schema (generated by ez-php/json-schema, the same one ez-php/openapi documents) the body must match:
$app->middlewareAlias('json-schema', JsonSchemaMiddleware::class); $router->post('/customers', [CustomerController::class, 'store']) ->middleware('json-schema:App\Requests\CreateCustomerRequest');
A failing body is answered with the framework's validation envelope (422), keyed in dot notation:
{"error": {"code": "VALIDATION_FAILED", "errors": {"email": ["Value does not match format \"email\"."], "address.city": ["Property \"city\" is required."]}}}
Pass a fixed schema instead with new JsonSchemaMiddleware(schema: [...]). Needs ez-php/contracts, ez-php/http and (for class schemas) ez-php/json-schema.
Not included
Remote $refs, the full 2020-12 vocabulary (oneOf, allOf, minLength, additionalProperties, ...), older drafts. For those, use a full-spec validator library.
License
MIT