Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0bef314
test: Enable draft-7 in tests
DannyvdSluijs Oct 10, 2025
6ad18f1
fix: Fix missing variable
DannyvdSluijs Oct 10, 2025
7d8e662
test: Force strict mode for draft 7
DannyvdSluijs Oct 10, 2025
e548737
fix: Use local copy for Draft 7 schema spec
DannyvdSluijs Oct 10, 2025
4189469
test: Pass draft identifier to testcase
DannyvdSluijs Oct 10, 2025
85fcd0c
feat: Copy draft07 from draft06 to get started
DannyvdSluijs Oct 10, 2025
6781c18
feat: Progress draft 7 support
DannyvdSluijs Oct 10, 2025
c47aae5
fix: Reset schema constraint after if evaluation
DannyvdSluijs Oct 10, 2025
fdf9ce3
style: Correct code style violations
DannyvdSluijs Oct 10, 2025
0411bbe
feat: Add content validation support
DannyvdSluijs Oct 10, 2025
c03528e
feat: Add idn hostname support
DannyvdSluijs Jan 16, 2026
26ff8a4
fix: Correct yielded name of cases data provider
DannyvdSluijs Feb 5, 2026
0bf41d5
fix: Fix validation for format="time"
DannyvdSluijs Feb 5, 2026
d978f28
style: Correct code style violations
DannyvdSluijs Feb 5, 2026
6e36524
fix: Scan for sub schemas within in array items
DannyvdSluijs Feb 5, 2026
ccd4681
test: Ignore test for draft7 which are ignored in previous drafts
DannyvdSluijs Feb 5, 2026
f153ddf
build: Add draft7 schema to the dist folder to return the correct sch…
DannyvdSluijs Feb 5, 2026
0fcd981
test: Skip draft7 for strict validation
DannyvdSluijs Feb 5, 2026
10190b4
refactor: Improve check and add false positive to baseline
DannyvdSluijs Feb 5, 2026
1fecd03
refactor: drop ext-intl as the approach for format="idn-hostname" cha…
DannyvdSluijs Feb 5, 2026
f9222fc
test: Re-enable other drafts for testing
DannyvdSluijs Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions dist/schema/json-schema-draft-07.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://json-schema.org/draft-07/schema#",
"title": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#"
}
},
"nonNegativeInteger": {
"type": "integer",
"minimum": 0
},
"nonNegativeIntegerDefault0": {
"allOf": [
{
"$ref": "#/definitions/nonNegativeInteger"
},
{
"default": 0
}
]
},
"simpleTypes": {
"enum": [
"array",
"boolean",
"integer",
"null",
"number",
"object",
"string"
]
},
"stringArray": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true,
"default": []
}
},
"type": [
"object",
"boolean"
],
"properties": {
"$id": {
"type": "string",
"format": "uri-reference"
},
"$schema": {
"type": "string",
"format": "uri"
},
"$ref": {
"type": "string",
"format": "uri-reference"
},
"$comment": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": true,
"readOnly": {
"type": "boolean",
"default": false
},
"writeOnly": {
"type": "boolean",
"default": false
},
"examples": {
"type": "array",
"items": true
},
"multipleOf": {
"type": "number",
"exclusiveMinimum": 0
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "number"
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "number"
},
"maxLength": {
"$ref": "#/definitions/nonNegativeInteger"
},
"minLength": {
"$ref": "#/definitions/nonNegativeIntegerDefault0"
},
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": {
"$ref": "#"
},
"items": {
"anyOf": [
{
"$ref": "#"
},
{
"$ref": "#/definitions/schemaArray"
}
],
"default": true
},
"maxItems": {
"$ref": "#/definitions/nonNegativeInteger"
},
"minItems": {
"$ref": "#/definitions/nonNegativeIntegerDefault0"
},
"uniqueItems": {
"type": "boolean",
"default": false
},
"contains": {
"$ref": "#"
},
"maxProperties": {
"$ref": "#/definitions/nonNegativeInteger"
},
"minProperties": {
"$ref": "#/definitions/nonNegativeIntegerDefault0"
},
"required": {
"$ref": "#/definitions/stringArray"
},
"additionalProperties": {
"$ref": "#"
},
"definitions": {
"type": "object",
"additionalProperties": {
"$ref": "#"
},
"default": {}
},
"properties": {
"type": "object",
"additionalProperties": {
"$ref": "#"
},
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": {
"$ref": "#"
},
"propertyNames": {
"format": "regex"
},
"default": {}
},
"dependencies": {
"type": "object",
"additionalProperties": {
"anyOf": [
{
"$ref": "#"
},
{
"$ref": "#/definitions/stringArray"
}
]
}
},
"propertyNames": {
"$ref": "#"
},
"const": true,
"enum": {
"type": "array",
"items": true,
"minItems": 1,
"uniqueItems": true
},
"type": {
"anyOf": [
{
"$ref": "#/definitions/simpleTypes"
},
{
"type": "array",
"items": {
"$ref": "#/definitions/simpleTypes"
},
"minItems": 1,
"uniqueItems": true
}
]
},
"format": {
"type": "string"
},
"contentMediaType": {
"type": "string"
},
"contentEncoding": {
"type": "string"
},
"if": {
"$ref": "#"
},
"then": {
"$ref": "#"
},
"else": {
"$ref": "#"
},
"allOf": {
"$ref": "#/definitions/schemaArray"
},
"anyOf": {
"$ref": "#/definitions/schemaArray"
},
"oneOf": {
"$ref": "#/definitions/schemaArray"
},
"not": {
"$ref": "#"
}
},
"default": true
}
7 changes: 6 additions & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ parameters:
count: 1
path: src/JsonSchema/Constraints/ConstraintInterface.php

-
message: "#^Strict comparison using \\=\\=\\= between non\\-empty\\-array\\<int, string\\> and false will always evaluate to false\\.$#"
count: 1
path: src/JsonSchema/Constraints/Drafts/Draft07/FormatConstraint.php

-
message: "#^Method JsonSchema\\\\Constraints\\\\Factory\\:\\:createInstanceFor\\(\\) should return JsonSchema\\\\Constraints\\\\BaseConstraint&JsonSchema\\\\Constraints\\\\ConstraintInterface but returns object\\.$#"
count: 1
Expand Down Expand Up @@ -641,7 +646,7 @@ parameters:
path: src/JsonSchema/SchemaStorage.php

-
message: "#^Call to function is_array\\(\\) with bool|object will always evaluate to false\\.$#"
message: "#^Call to function is_array\\(\\) with bool\\|object will always evaluate to false\\.$#"
count: 1
path: src/JsonSchema/SchemaStorage.php

Expand Down
6 changes: 5 additions & 1 deletion src/JsonSchema/ConstraintError.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class ConstraintError extends Enum
public const PROPERTY_NAMES = 'propertyNames';
public const TYPE = 'type';
public const UNIQUE_ITEMS = 'uniqueItems';
public const CONTENT_MEDIA_TYPE = 'contentMediaType';
public const CONTENT_ENCODING = 'contentEncoding';

/**
* @return string
Expand Down Expand Up @@ -115,7 +117,9 @@ public function getMessage()
self::PROPERTIES_MAX => 'Must contain no more than %d properties',
self::PROPERTY_NAMES => 'Property name %s is invalid',
self::TYPE => '%s value found, but %s is required',
self::UNIQUE_ITEMS => 'There are no duplicates allowed in the array'
self::UNIQUE_ITEMS => 'There are no duplicates allowed in the array',
self::CONTENT_MEDIA_TYPE => 'Value is not valid with content media type',
self::CONTENT_ENCODING => 'Value is not valid with content encoding',
];

if (!isset($messages[$name])) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace JsonSchema\Constraints\Drafts\Draft07;

use JsonSchema\ConstraintError;
use JsonSchema\Constraints\ConstraintInterface;
use JsonSchema\Entity\ErrorBagProxy;
use JsonSchema\Entity\JsonPointer;

class AdditionalItemsConstraint implements ConstraintInterface
{
use ErrorBagProxy;

/** @var Factory */
private $factory;

public function __construct(?Factory $factory = null)
{
$this->factory = $factory ?: new Factory();
$this->initialiseErrorBag($this->factory);
}

public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void
{
if (!property_exists($schema, 'additionalItems')) {
return;
}

if ($schema->additionalItems === true) {
return;
}
if ($schema->additionalItems === false && !property_exists($schema, 'items')) {
return;
}

if (!is_array($value)) {
return;
}
if (!property_exists($schema, 'items')) {
return;
}
if (property_exists($schema, 'items') && is_object($schema->items)) {
return;
}

$additionalItems = array_diff_key($value, property_exists($schema, 'items') ? $schema->items : []);

foreach ($additionalItems as $propertyName => $propertyValue) {
$schemaConstraint = $this->factory->createInstanceFor('schema');
$schemaConstraint->check($propertyValue, $schema->additionalItems, $path, $i);

if ($schemaConstraint->isValid()) {
continue;
}

$this->addError(ConstraintError::ADDITIONAL_ITEMS(), $path, ['item' => $i, 'property' => $propertyName, 'additionalItems' => $schema->additionalItems]);
}
}
}
Loading