From ad1790e06e7ca173e05a7c03285608ae38c25849 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Wed, 7 Jan 2026 14:14:42 +0100 Subject: [PATCH] chore: bump justinrainbow/json-schema to 6.6.4 Signed-off-by: grnd-alt --- composer.json | 1 + composer.lock | 18 +- composer/autoload_classmap.php | 36 +++ composer/autoload_static.php | 36 +++ composer/installed.json | 21 +- composer/installed.php | 6 +- .../dist/schema/json-schema-draft-06.json | 155 ++++++++++++ .../src/JsonSchema/ConstraintError.php | 10 + .../src/JsonSchema/Constraints/Constraint.php | 1 + .../Draft06/AdditionalItemsConstraint.php | 61 +++++ .../AdditionalPropertiesConstraint.php | 93 ++++++++ .../Drafts/Draft06/AllOfConstraint.php | 42 ++++ .../Drafts/Draft06/AnyOfConstraint.php | 51 ++++ .../Drafts/Draft06/ConstConstraint.php | 35 +++ .../Drafts/Draft06/ContainsConstraint.php | 47 ++++ .../Drafts/Draft06/DependenciesConstraint.php | 64 +++++ .../Drafts/Draft06/Draft06Constraint.php | 81 +++++++ .../Drafts/Draft06/EnumConstraint.php | 41 ++++ .../Draft06/ExclusiveMaximumConstraint.php | 38 +++ .../Draft06/ExclusiveMinimumConstraint.php | 38 +++ .../Constraints/Drafts/Draft06/Factory.php | 46 ++++ .../Drafts/Draft06/FormatConstraint.php | 220 ++++++++++++++++++ .../Drafts/Draft06/ItemsConstraint.php | 52 +++++ .../Drafts/Draft06/MaxItemsConstraint.php | 39 ++++ .../Drafts/Draft06/MaxLengthConstraint.php | 39 ++++ .../Draft06/MaxPropertiesConstraint.php | 39 ++++ .../Drafts/Draft06/MaximumConstraint.php | 38 +++ .../Drafts/Draft06/MinItemsConstraint.php | 39 ++++ .../Drafts/Draft06/MinLengthConstraint.php | 39 ++++ .../Draft06/MinPropertiesConstraint.php | 39 ++++ .../Drafts/Draft06/MinimumConstraint.php | 38 +++ .../Drafts/Draft06/MultipleOfConstraint.php | 54 +++++ .../Drafts/Draft06/NotConstraint.php | 40 ++++ .../Drafts/Draft06/OneOfConstraint.php | 50 ++++ .../Drafts/Draft06/PatternConstraint.php | 63 +++++ .../Draft06/PatternPropertiesConstraint.php | 72 ++++++ .../Drafts/Draft06/PropertiesConstraint.php | 48 ++++ .../Draft06/PropertiesNamesConstraint.php | 65 ++++++ .../Drafts/Draft06/RefConstraint.php | 45 ++++ .../Drafts/Draft06/RequiredConstraint.php | 58 +++++ .../Drafts/Draft06/TypeConstraint.php | 50 ++++ .../Drafts/Draft06/UniqueItemsConstraint.php | 48 ++++ .../src/JsonSchema/Constraints/Factory.php | 27 ++- .../Constraints/SchemaConstraint.php | 3 +- .../Constraints/UndefinedConstraint.php | 11 + .../src/JsonSchema/DraftIdentifiers.php | 48 ++++ .../src/JsonSchema/Entity/ErrorBag.php | 109 +++++++++ .../src/JsonSchema/Entity/ErrorBagProxy.php | 66 ++++++ .../json-schema/src/JsonSchema/Rfc3339.php | 46 +++- .../src/JsonSchema/SchemaStorage.php | 48 +++- .../src/JsonSchema/SchemaStorageInterface.php | 8 +- .../Validator/RelativeReferenceValidator.php | 4 + .../Tool/Validator/UriValidator.php | 22 +- .../src/JsonSchema/Uri/Retrievers/Curl.php | 4 +- .../Uri/Retrievers/FileGetContents.php | 25 +- .../src/JsonSchema/Uri/UriRetriever.php | 2 +- .../json-schema/src/JsonSchema/Validator.php | 30 ++- 57 files changed, 2481 insertions(+), 68 deletions(-) create mode 100644 justinrainbow/json-schema/dist/schema/json-schema-draft-06.json create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalPropertiesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AllOfConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AnyOfConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ConstConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/EnumConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMaximumConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMinimumConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Factory.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxItemsConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxLengthConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxPropertiesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaximumConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinItemsConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinLengthConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinPropertiesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinimumConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/NotConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/OneOfConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesNamesConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RefConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/TypeConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/UniqueItemsConstraint.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/DraftIdentifiers.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBag.php create mode 100644 justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBagProxy.php diff --git a/composer.json b/composer.json index 75dd77bc1..9b8fcf2e5 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "icewind/searchdav": "^3.2.0", "icewind/smb": "^3.8.1", "icewind/streams": "^0.7.8", + "justinrainbow/json-schema": "^6.6", "kornrunner/blurhash": "^1.2", "laravel/serializable-closure": "^2.0.4", "mexitek/phpcolors": "^1.0", diff --git a/composer.lock b/composer.lock index 08f54666f..e7ef3a780 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5e4f2ab3bfeb38f0ab586de491e01a2e", + "content-hash": "fde916feea46ab0fd4bad170c03bf3c8", "packages": [ { "name": "aws/aws-crt-php", @@ -1513,26 +1513,26 @@ }, { "name": "justinrainbow/json-schema", - "version": "6.4.2", + "version": "6.6.4", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02" + "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/ce1fd2d47799bb60668643bc6220f6278a4c1d02", - "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2eeb75d21cf73211335888e7f5e6fd7440723ec7", + "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7", "shasum": "" }, "require": { "ext-json": "*", - "marc-mabe/php-enum": "^4.0", + "marc-mabe/php-enum": "^4.4", "php": "^7.2 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.3.0", - "json-schema/json-schema-test-suite": "1.2.0", + "json-schema/json-schema-test-suite": "^23.2", "marc-mabe/php-enum-phpstan": "^2.0", "phpspec/prophecy": "^1.19", "phpstan/phpstan": "^1.12", @@ -1582,9 +1582,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.4.2" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.4" }, - "time": "2025-06-03T18:27:04+00:00" + "time": "2025-12-19T15:01:32+00:00" }, { "name": "kornrunner/blurhash", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 804843f14..47b4b4fec 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -1168,6 +1168,39 @@ 'JsonSchema\\Constraints\\ConstConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstConstraint.php', 'JsonSchema\\Constraints\\Constraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php', 'JsonSchema\\Constraints\\ConstraintInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AdditionalItemsConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AdditionalPropertiesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AllOfConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AllOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AnyOfConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AnyOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ConstConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ConstConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ContainsConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\DependenciesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\Draft06Constraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\EnumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/EnumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ExclusiveMaximumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMaximumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ExclusiveMinimumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMinimumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\Factory' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Factory.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\FormatConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ItemsConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaxItemsConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaxLengthConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxLengthConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaxPropertiesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaximumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaximumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinItemsConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinLengthConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinLengthConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinPropertiesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinimumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinimumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MultipleOfConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\NotConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/NotConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\OneOfConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/OneOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PatternConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PatternPropertiesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PropertiesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PropertiesNamesConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesNamesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\RefConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RefConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\RequiredConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\TypeConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/TypeConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\UniqueItemsConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/UniqueItemsConstraint.php', 'JsonSchema\\Constraints\\EnumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php', 'JsonSchema\\Constraints\\Factory' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php', 'JsonSchema\\Constraints\\FormatConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php', @@ -1180,6 +1213,9 @@ 'JsonSchema\\Constraints\\TypeCheck\\TypeCheckInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php', 'JsonSchema\\Constraints\\TypeConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php', 'JsonSchema\\Constraints\\UndefinedConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php', + 'JsonSchema\\DraftIdentifiers' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/DraftIdentifiers.php', + 'JsonSchema\\Entity\\ErrorBag' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBag.php', + 'JsonSchema\\Entity\\ErrorBagProxy' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBagProxy.php', 'JsonSchema\\Entity\\JsonPointer' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php', 'JsonSchema\\Enum' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Enum.php', 'JsonSchema\\Exception\\ExceptionInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php', diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 5491167ac..7c435478c 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -1700,6 +1700,39 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'JsonSchema\\Constraints\\ConstConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstConstraint.php', 'JsonSchema\\Constraints\\Constraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php', 'JsonSchema\\Constraints\\ConstraintInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AdditionalItemsConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AdditionalPropertiesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AllOfConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AllOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\AnyOfConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AnyOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ConstConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ConstConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ContainsConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\DependenciesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\Draft06Constraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\EnumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/EnumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ExclusiveMaximumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMaximumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ExclusiveMinimumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMinimumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\Factory' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Factory.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\FormatConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\ItemsConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaxItemsConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaxLengthConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxLengthConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaxPropertiesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MaximumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaximumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinItemsConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinItemsConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinLengthConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinLengthConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinPropertiesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MinimumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinimumConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\MultipleOfConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\NotConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/NotConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\OneOfConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/OneOfConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PatternConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PatternPropertiesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PropertiesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\PropertiesNamesConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesNamesConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\RefConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RefConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\RequiredConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\TypeConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/TypeConstraint.php', + 'JsonSchema\\Constraints\\Drafts\\Draft06\\UniqueItemsConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/UniqueItemsConstraint.php', 'JsonSchema\\Constraints\\EnumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php', 'JsonSchema\\Constraints\\Factory' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php', 'JsonSchema\\Constraints\\FormatConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php', @@ -1712,6 +1745,9 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'JsonSchema\\Constraints\\TypeCheck\\TypeCheckInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php', 'JsonSchema\\Constraints\\TypeConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php', 'JsonSchema\\Constraints\\UndefinedConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php', + 'JsonSchema\\DraftIdentifiers' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/DraftIdentifiers.php', + 'JsonSchema\\Entity\\ErrorBag' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBag.php', + 'JsonSchema\\Entity\\ErrorBagProxy' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBagProxy.php', 'JsonSchema\\Entity\\JsonPointer' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php', 'JsonSchema\\Enum' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Enum.php', 'JsonSchema\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php', diff --git a/composer/installed.json b/composer/installed.json index 162a7b7aa..543382615 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -1570,33 +1570,33 @@ }, { "name": "justinrainbow/json-schema", - "version": "6.4.2", - "version_normalized": "6.4.2.0", + "version": "6.6.4", + "version_normalized": "6.6.4.0", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02" + "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/ce1fd2d47799bb60668643bc6220f6278a4c1d02", - "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2eeb75d21cf73211335888e7f5e6fd7440723ec7", + "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7", "shasum": "" }, "require": { "ext-json": "*", - "marc-mabe/php-enum": "^4.0", + "marc-mabe/php-enum": "^4.4", "php": "^7.2 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.3.0", - "json-schema/json-schema-test-suite": "1.2.0", + "json-schema/json-schema-test-suite": "^23.2", "marc-mabe/php-enum-phpstan": "^2.0", "phpspec/prophecy": "^1.19", "phpstan/phpstan": "^1.12", "phpunit/phpunit": "^8.5" }, - "time": "2025-06-03T18:27:04+00:00", + "time": "2025-12-19T15:01:32+00:00", "bin": [ "bin/validate-json" ], @@ -1642,7 +1642,7 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.4.2" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.4" }, "install-path": "../justinrainbow/json-schema" }, @@ -3978,7 +3978,8 @@ "dev-master": "4.0.x-dev" }, "patches_applied": { - "fix use RDATE in time range check and use all instances": ".patches/sabre-vobject-rdate.patch" + "fix use RDATE in time range check and use all instances": ".patches/sabre-vobject-rdate.patch", + "fix send participation reply on fresh event": ".patches/sabre-vobject-iTipBroker-replies.patch" } }, "installation-source": "dist", diff --git a/composer/installed.php b/composer/installed.php index 4ed4bf002..ebc27c628 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -200,9 +200,9 @@ 'dev_requirement' => false, ), 'justinrainbow/json-schema' => array( - 'pretty_version' => '6.4.2', - 'version' => '6.4.2.0', - 'reference' => 'ce1fd2d47799bb60668643bc6220f6278a4c1d02', + 'pretty_version' => '6.6.4', + 'version' => '6.6.4.0', + 'reference' => '2eeb75d21cf73211335888e7f5e6fd7440723ec7', 'type' => 'library', 'install_path' => __DIR__ . '/../justinrainbow/json-schema', 'aliases' => array(), diff --git a/justinrainbow/json-schema/dist/schema/json-schema-draft-06.json b/justinrainbow/json-schema/dist/schema/json-schema-draft-06.json new file mode 100644 index 000000000..bd3e763bc --- /dev/null +++ b/justinrainbow/json-schema/dist/schema/json-schema-draft-06.json @@ -0,0 +1,155 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/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" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "examples": { + "type": "array", + "items": {} + }, + "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": {} + }, + "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": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} +} diff --git a/justinrainbow/json-schema/src/JsonSchema/ConstraintError.php b/justinrainbow/json-schema/src/JsonSchema/ConstraintError.php index c17cfeff1..ed6cf03e0 100644 --- a/justinrainbow/json-schema/src/JsonSchema/ConstraintError.php +++ b/justinrainbow/json-schema/src/JsonSchema/ConstraintError.php @@ -17,8 +17,10 @@ class ConstraintError extends Enum public const DIVISIBLE_BY = 'divisibleBy'; public const ENUM = 'enum'; public const CONSTANT = 'const'; + public const CONTAINS = 'contains'; public const EXCLUSIVE_MINIMUM = 'exclusiveMinimum'; public const EXCLUSIVE_MAXIMUM = 'exclusiveMaximum'; + public const FALSE = 'false'; public const FORMAT_COLOR = 'colorFormat'; public const FORMAT_DATE = 'dateFormat'; public const FORMAT_DATE_TIME = 'dateTimeFormat'; @@ -26,10 +28,12 @@ class ConstraintError extends Enum public const FORMAT_EMAIL = 'emailFormat'; public const FORMAT_HOSTNAME = 'styleHostName'; public const FORMAT_IP = 'ipFormat'; + public const FORMAT_JSON_POINTER = 'jsonPointerFormat'; public const FORMAT_PHONE = 'phoneFormat'; public const FORMAT_REGEX= 'regexFormat'; public const FORMAT_STYLE = 'styleFormat'; public const FORMAT_TIME = 'timeFormat'; + public const FORMAT_URI_TEMPLATE = 'uriTemplateFormat'; public const FORMAT_URL = 'urlFormat'; public const FORMAT_URL_REF = 'urlRefFormat'; public const INVALID_SCHEMA = 'invalidSchema'; @@ -51,6 +55,7 @@ class ConstraintError extends Enum public const PREGEX_INVALID = 'pregrex'; public const PROPERTIES_MIN = 'minProperties'; public const PROPERTIES_MAX = 'maxProperties'; + public const PROPERTY_NAMES = 'propertyNames'; public const TYPE = 'type'; public const UNIQUE_ITEMS = 'uniqueItems'; @@ -70,8 +75,10 @@ public function getMessage() self::DIVISIBLE_BY => 'Is not divisible by %d', self::ENUM => 'Does not have a value in the enumeration %s', self::CONSTANT => 'Does not have a value equal to %s', + self::CONTAINS => 'Does not have a value valid to contains schema', self::EXCLUSIVE_MINIMUM => 'Must have a minimum value greater than %d', self::EXCLUSIVE_MAXIMUM => 'Must have a maximum value less than %d', + self::FALSE => 'Boolean schema false', self::FORMAT_COLOR => 'Invalid color', self::FORMAT_DATE => 'Invalid date %s, expected format YYYY-MM-DD', self::FORMAT_DATE_TIME => 'Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', @@ -79,10 +86,12 @@ public function getMessage() self::FORMAT_EMAIL => 'Invalid email', self::FORMAT_HOSTNAME => 'Invalid hostname', self::FORMAT_IP => 'Invalid IP address', + self::FORMAT_JSON_POINTER => 'Invalid JSON pointer', self::FORMAT_PHONE => 'Invalid phone number', self::FORMAT_REGEX=> 'Invalid regex format %s', self::FORMAT_STYLE => 'Invalid style', self::FORMAT_TIME => 'Invalid time %s, expected format hh:mm:ss', + self::FORMAT_URI_TEMPLATE => 'Invalid URI template format', self::FORMAT_URL => 'Invalid URL format', self::FORMAT_URL_REF => 'Invalid URL reference format', self::LENGTH_MAX => 'Must be at most %d characters long', @@ -104,6 +113,7 @@ public function getMessage() self::PREGEX_INVALID => 'The pattern %s is invalid', self::PROPERTIES_MIN => 'Must contain a minimum of %d properties', 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' ]; diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php index 8e818f0aa..3c7e824b1 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php @@ -21,6 +21,7 @@ abstract class Constraint extends BaseConstraint implements ConstraintInterface public const CHECK_MODE_EARLY_COERCE = 0x00000040; public const CHECK_MODE_ONLY_REQUIRED_DEFAULTS = 0x00000080; public const CHECK_MODE_VALIDATE_SCHEMA = 0x00000100; + public const CHECK_MODE_STRICT = 0x00000200; /** * Bubble down the path diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php new file mode 100644 index 000000000..d1adbce8f --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php @@ -0,0 +1,61 @@ +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]); + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalPropertiesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalPropertiesConstraint.php new file mode 100644 index 000000000..5f2448961 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AdditionalPropertiesConstraint.php @@ -0,0 +1,93 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'additionalProperties')) { + return; + } + + if ($schema->additionalProperties === true) { + return; + } + + if (!is_object($value)) { + return; + } + + $additionalProperties = get_object_vars($value); + + if (isset($schema->properties)) { + $additionalProperties = array_diff_key($additionalProperties, (array) $schema->properties); + } + + if (isset($schema->patternProperties)) { + $patterns = array_keys(get_object_vars($schema->patternProperties)); + + foreach ($additionalProperties as $key => $_) { + foreach ($patterns as $pattern) { + if (preg_match($this->createPregMatchPattern($pattern), (string) $key)) { + unset($additionalProperties[$key]); + break; + } + } + } + } + + if (is_object($schema->additionalProperties)) { + foreach ($additionalProperties as $key => $additionalPropertiesValue) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($additionalPropertiesValue, $schema->additionalProperties, $path, $i); // @todo increment path + if ($schemaConstraint->isValid()) { + unset($additionalProperties[$key]); + } + } + } + + foreach ($additionalProperties as $key => $additionalPropertiesValue) { + $this->addError(ConstraintError::ADDITIONAL_PROPERTIES(), $path, ['found' => $additionalPropertiesValue]); + } + } + + private function createPregMatchPattern(string $pattern): string + { + $replacements = [ +// '\D' => '[^0-9]', +// '\d' => '[0-9]', + '\p{digit}' => '\p{Nd}', +// '\w' => '[A-Za-z0-9_]', +// '\W' => '[^A-Za-z0-9_]', +// '\s' => '[\s\x{200B}]' // Explicitly include zero width white space, + '\p{Letter}' => '\p{L}', // Map ECMA long property name to PHP (PCRE) Unicode property abbreviations + ]; + + $pattern = str_replace( + array_keys($replacements), + array_values($replacements), + $pattern + ); + + return '/' . str_replace('/', '\/', $pattern) . '/u'; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AllOfConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AllOfConstraint.php new file mode 100644 index 000000000..5a69f657d --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AllOfConstraint.php @@ -0,0 +1,42 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'allOf')) { + return; + } + + foreach ($schema->allOf as $allOfSchema) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($value, $allOfSchema, $path, $i); + + if ($schemaConstraint->isValid()) { + continue; + } + $this->addError(ConstraintError::ALL_OF(), $path); + $this->addErrors($schemaConstraint->getErrors()); + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AnyOfConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AnyOfConstraint.php new file mode 100644 index 000000000..29b0f29ba --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/AnyOfConstraint.php @@ -0,0 +1,51 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'anyOf')) { + return; + } + + foreach ($schema->anyOf as $anyOfSchema) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + + try { + $schemaConstraint->check($value, $anyOfSchema, $path, $i); + + if ($schemaConstraint->isValid()) { + $this->errorBag()->reset(); + + return; + } + + $this->addErrors($schemaConstraint->getErrors()); + } catch (ValidationException $e) { + } + } + + $this->addError(ConstraintError::ANY_OF(), $path); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ConstConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ConstConstraint.php new file mode 100644 index 000000000..563bd42c5 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ConstConstraint.php @@ -0,0 +1,35 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'const')) { + return; + } + + if (DeepComparer::isEqual($value, $schema->const)) { + return; + } + + $this->addError(ConstraintError::CONSTANT(), $path, ['const' => $schema->const]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php new file mode 100644 index 000000000..45f0785d9 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php @@ -0,0 +1,47 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'contains')) { + return; + } + + $properties = []; + if (!is_array($value)) { + return; + } + + foreach ($value as $propertyName => $propertyValue) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + + $schemaConstraint->check($propertyValue, $schema->contains, $path, $i); + if ($schemaConstraint->isValid()) { + return; + } + } + + $this->addError(ConstraintError::CONTAINS(), $path, ['contains' => $schema->contains]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php new file mode 100644 index 000000000..5b8e86b4e --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php @@ -0,0 +1,64 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'dependencies')) { + return; + } + + if (!is_object($value)) { + return; + } + + foreach ($schema->dependencies as $dependant => $dependencies) { + if (!property_exists($value, $dependant)) { + continue; + } + if ($dependencies === true) { + continue; + } + if ($dependencies === false) { + $this->addError(ConstraintError::FALSE(), $path, ['dependant' => $dependant]); + continue; + } + + if (is_array($dependencies)) { + foreach ($dependencies as $dependency) { + if (property_exists($value, $dependant) && !property_exists($value, $dependency)) { + $this->addError(ConstraintError::DEPENDENCIES(), $path, ['dependant' => $dependant, 'dependency' => $dependency]); + } + } + } + + if (is_object($dependencies)) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($value, $dependencies, $path, $i); + if (!$schemaConstraint->isValid()) { + $this->addErrors($schemaConstraint->getErrors()); + } + } + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php new file mode 100644 index 000000000..bd6efac66 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php @@ -0,0 +1,81 @@ +getSchemaStorage() : new SchemaStorage(), + $factory ? $factory->getUriRetriever() : new UriRetriever(), + $factory ? $factory->getConfig() : Constraint::CHECK_MODE_NORMAL + )); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (is_bool($schema)) { + if ($schema === false) { + $this->addError(ConstraintError::FALSE(), $path, []); + } + + return; + } + + // Apply defaults + $this->checkForKeyword('ref', $value, $schema, $path, $i); + $this->checkForKeyword('required', $value, $schema, $path, $i); + $this->checkForKeyword('contains', $value, $schema, $path, $i); + $this->checkForKeyword('properties', $value, $schema, $path, $i); + $this->checkForKeyword('propertyNames', $value, $schema, $path, $i); + $this->checkForKeyword('patternProperties', $value, $schema, $path, $i); + $this->checkForKeyword('type', $value, $schema, $path, $i); + $this->checkForKeyword('not', $value, $schema, $path, $i); + $this->checkForKeyword('dependencies', $value, $schema, $path, $i); + $this->checkForKeyword('allOf', $value, $schema, $path, $i); + $this->checkForKeyword('anyOf', $value, $schema, $path, $i); + $this->checkForKeyword('oneOf', $value, $schema, $path, $i); + + $this->checkForKeyword('additionalProperties', $value, $schema, $path, $i); + $this->checkForKeyword('items', $value, $schema, $path, $i); + $this->checkForKeyword('additionalItems', $value, $schema, $path, $i); + $this->checkForKeyword('uniqueItems', $value, $schema, $path, $i); + $this->checkForKeyword('minItems', $value, $schema, $path, $i); + $this->checkForKeyword('minProperties', $value, $schema, $path, $i); + $this->checkForKeyword('maxProperties', $value, $schema, $path, $i); + $this->checkForKeyword('minimum', $value, $schema, $path, $i); + $this->checkForKeyword('maximum', $value, $schema, $path, $i); + $this->checkForKeyword('minLength', $value, $schema, $path, $i); + $this->checkForKeyword('exclusiveMinimum', $value, $schema, $path, $i); + $this->checkForKeyword('maxItems', $value, $schema, $path, $i); + $this->checkForKeyword('maxLength', $value, $schema, $path, $i); + $this->checkForKeyword('exclusiveMaximum', $value, $schema, $path, $i); + $this->checkForKeyword('enum', $value, $schema, $path, $i); + $this->checkForKeyword('const', $value, $schema, $path, $i); + $this->checkForKeyword('multipleOf', $value, $schema, $path, $i); + $this->checkForKeyword('format', $value, $schema, $path, $i); + $this->checkForKeyword('pattern', $value, $schema, $path, $i); + } + + /** + * @param mixed $value + * @param mixed $schema + * @param mixed $i + */ + protected function checkForKeyword(string $keyword, $value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + $validator = $this->factory->createInstanceFor($keyword); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/EnumConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/EnumConstraint.php new file mode 100644 index 000000000..1ed3b65ad --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/EnumConstraint.php @@ -0,0 +1,41 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'enum')) { + return; + } + + foreach ($schema->enum as $enumCase) { + if (DeepComparer::isEqual($value, $enumCase)) { + return; + } + + if (is_numeric($value) && is_numeric($enumCase) && DeepComparer::isEqual((float) $value, (float) $enumCase)) { + return; + } + } + + $this->addError(ConstraintError::ENUM(), $path, ['enum' => $schema->enum]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMaximumConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMaximumConstraint.php new file mode 100644 index 000000000..2d29a175a --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMaximumConstraint.php @@ -0,0 +1,38 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'exclusiveMaximum')) { + return; + } + + if (!is_numeric($value)) { + return; + } + + if ($value < $schema->exclusiveMaximum) { + return; + } + + $this->addError(ConstraintError::EXCLUSIVE_MAXIMUM(), $path, ['exclusiveMaximum' => $schema->exclusiveMaximum, 'found' => $value]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMinimumConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMinimumConstraint.php new file mode 100644 index 000000000..b167d198f --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ExclusiveMinimumConstraint.php @@ -0,0 +1,38 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'exclusiveMinimum')) { + return; + } + + if (!is_numeric($value)) { + return; + } + + if ($value > $schema->exclusiveMinimum) { + return; + } + + $this->addError(ConstraintError::EXCLUSIVE_MINIMUM(), $path, ['exclusiveMinimum' => $schema->exclusiveMinimum, 'found' => $value]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Factory.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Factory.php new file mode 100644 index 000000000..1d23f9fff --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/Factory.php @@ -0,0 +1,46 @@ + + */ + protected $constraintMap = [ + 'schema' => Draft06Constraint::class, + 'additionalProperties' => AdditionalPropertiesConstraint::class, + 'additionalItems' => AdditionalItemsConstraint::class, + 'dependencies' => DependenciesConstraint::class, + 'type' => TypeConstraint::class, + 'const' => ConstConstraint::class, + 'enum' => EnumConstraint::class, + 'uniqueItems' => UniqueItemsConstraint::class, + 'minItems' => MinItemsConstraint::class, + 'minProperties' => MinPropertiesConstraint::class, + 'maxProperties' => MaxPropertiesConstraint::class, + 'minimum' => MinimumConstraint::class, + 'maximum' => MaximumConstraint::class, + 'exclusiveMinimum' => ExclusiveMinimumConstraint::class, + 'minLength' => MinLengthConstraint::class, + 'maxLength' => MaxLengthConstraint::class, + 'maxItems' => MaxItemsConstraint::class, + 'exclusiveMaximum' => ExclusiveMaximumConstraint::class, + 'multipleOf' => MultipleOfConstraint::class, + 'required' => RequiredConstraint::class, + 'format' => FormatConstraint::class, + 'anyOf' => AnyOfConstraint::class, + 'allOf' => AllOfConstraint::class, + 'oneOf' => OneOfConstraint::class, + 'not' => NotConstraint::class, + 'contains' => ContainsConstraint::class, + 'propertyNames' => PropertiesNamesConstraint::class, + 'patternProperties' => PatternPropertiesConstraint::class, + 'pattern' => PatternConstraint::class, + 'properties' => PropertiesConstraint::class, + 'items' => ItemsConstraint::class, + 'ref' => RefConstraint::class, + ]; +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php new file mode 100644 index 000000000..578a27c37 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php @@ -0,0 +1,220 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'format')) { + return; + } + + if (!is_string($value)) { + return; + } + + switch ($schema->format) { + case 'date': + if (!$this->validateDateTime($value, 'Y-m-d')) { + $this->addError(ConstraintError::FORMAT_DATE(), $path, ['date' => $value, 'format' => $schema->format]); + } + break; + case 'time': + if (!$this->validateDateTime($value, 'H:i:s')) { + $this->addError(ConstraintError::FORMAT_TIME(), $path, ['time' => $value, 'format' => $schema->format]); + } + break; + case 'date-time': + if (!$this->validateRfc3339DateTime($value)) { + $this->addError(ConstraintError::FORMAT_DATE_TIME(), $path, ['dateTime' => $value, 'format' => $schema->format]); + } + break; + case 'utc-millisec': + if (!$this->validateDateTime($value, 'U')) { + $this->addError(ConstraintError::FORMAT_DATE_UTC(), $path, ['value' => $value, 'format' => $schema->format]); + } + break; + case 'regex': + if (!$this->validateRegex($value)) { + $this->addError(ConstraintError::FORMAT_REGEX(), $path, ['value' => $value, 'format' => $schema->format]); + } + break; + case 'ip-address': + case 'ipv4': + if (filter_var($value, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4) === null) { + $this->addError(ConstraintError::FORMAT_IP(), $path, ['format' => $schema->format]); + } + break; + case 'ipv6': + if (filter_var($value, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6) === null) { + $this->addError(ConstraintError::FORMAT_IP(), $path, ['format' => $schema->format]); + } + break; + case 'color': + if (!$this->validateColor($value)) { + $this->addError(ConstraintError::FORMAT_COLOR(), $path, ['format' => $schema->format]); + } + break; + case 'style': + if (!$this->validateStyle($value)) { + $this->addError(ConstraintError::FORMAT_STYLE(), $path, ['format' => $schema->format]); + } + break; + case 'phone': + if (!$this->validatePhone($value)) { + $this->addError(ConstraintError::FORMAT_PHONE(), $path, ['format' => $schema->format]); + } + break; + case 'uri': + if (!UriValidator::isValid($value)) { + $this->addError(ConstraintError::FORMAT_URL(), $path, ['format' => $schema->format]); + } + break; + + case 'uriref': + case 'uri-reference': + if (!(UriValidator::isValid($value) || RelativeReferenceValidator::isValid($value))) { + $this->addError(ConstraintError::FORMAT_URL(), $path, ['format' => $schema->format]); + } + break; + case 'uri-template': + if (!$this->validateUriTemplate($value)) { + $this->addError(ConstraintError::FORMAT_URI_TEMPLATE(), $path, ['format' => $schema->format]); + } + break; + + case 'email': + if (filter_var($value, FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE | FILTER_FLAG_EMAIL_UNICODE) === null) { + $this->addError(ConstraintError::FORMAT_EMAIL(), $path, ['format' => $schema->format]); + } + break; + case 'host-name': + case 'hostname': + if (!$this->validateHostname($value)) { + $this->addError(ConstraintError::FORMAT_HOSTNAME(), $path, ['format' => $schema->format]); + } + break; + case 'json-pointer': + if (!$this->validateJsonPointer($value)) { + $this->addError(ConstraintError::FORMAT_JSON_POINTER(), $path, ['format' => $schema->format]); + } + break; + default: + break; + } + } + + private function validateDateTime(string $datetime, string $format): bool + { + $dt = \DateTime::createFromFormat($format, $datetime); + + if (!$dt) { + return false; + } + + return $datetime === $dt->format($format); + } + + private function validateRegex(string $regex): bool + { + return preg_match(self::jsonPatternToPhpRegex($regex), '') !== false; + } + + /** + * Transform a JSON pattern into a PCRE regex + */ + private static function jsonPatternToPhpRegex(string $pattern): string + { + return '~' . str_replace('~', '\\~', $pattern) . '~u'; + } + + private function validateColor(string $color): bool + { + if (in_array(strtolower($color), ['aqua', 'black', 'blue', 'fuchsia', + 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple', + 'red', 'silver', 'teal', 'white', 'yellow'])) { + return true; + } + + return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color) !== false; + } + + private function validateStyle(string $style): bool + { + $properties = explode(';', rtrim($style, ';')); + $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT); + + return empty($invalidEntries); + } + + private function validatePhone(string $phone): bool + { + return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone) !== false; + } + + private function validateHostname(string $host): bool + { + $hostnameRegex = '/^(?!-)(?!.*?[^A-Za-z0-9\-\.])(?:(?!-)[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)*(?!-)[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?$/'; + + return preg_match($hostnameRegex, $host) === 1; + } + + private function validateJsonPointer(string $value): bool + { + // Must be empty or start with a forward slash + if ($value !== '' && $value[0] !== '/') { + return false; + } + + // Split into reference tokens and check for invalid escape sequences + $tokens = explode('/', $value); + array_shift($tokens); // remove leading empty part due to leading slash + + foreach ($tokens as $token) { + // "~" must only be followed by "0" or "1" + if (preg_match('/~(?![01])/', $token)) { + return false; + } + } + + return true; + } + + private function validateRfc3339DateTime(string $value): bool + { + $dateTime = Rfc3339::createFromString($value); + if (is_null($dateTime)) { + return false; + } + + // Compare value and date result to be equal + return true; + } + + private function validateUriTemplate(string $value): bool + { + return preg_match( + '/^(?:[^\{\}]*|\{[a-zA-Z0-9_:%\/\.~\-\+\*]+\})*$/', + $value + ) === 1; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php new file mode 100644 index 000000000..21259e1a8 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php @@ -0,0 +1,52 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'items')) { + return; + } + + if (!is_array($value)) { + return; + } + + foreach ($value as $propertyName => $propertyValue) { + $itemSchema = $schema->items; + if (is_array($itemSchema)) { + if (!array_key_exists($propertyName, $itemSchema)) { + continue; + } + + $itemSchema = $itemSchema[$propertyName]; + } + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($propertyValue, $itemSchema, $path, $i); + if ($schemaConstraint->isValid()) { + continue; + } + + $this->addErrors($schemaConstraint->getErrors()); + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxItemsConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxItemsConstraint.php new file mode 100644 index 000000000..d7ad2649f --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxItemsConstraint.php @@ -0,0 +1,39 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'maxItems')) { + return; + } + + if (!is_array($value)) { + return; + } + + $count = count($value); + if ($count <= $schema->maxItems) { + return; + } + + $this->addError(ConstraintError::MAX_ITEMS(), $path, ['maxItems' => $schema->maxItems, 'found' => $count]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxLengthConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxLengthConstraint.php new file mode 100644 index 000000000..5243f488f --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxLengthConstraint.php @@ -0,0 +1,39 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'maxLength')) { + return; + } + + if (!is_string($value)) { + return; + } + + $length = mb_strlen($value); + if ($length <= $schema->maxLength) { + return; + } + + $this->addError(ConstraintError::LENGTH_MAX(), $path, ['maxLength' => $schema->maxLength, 'found' => $length]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxPropertiesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxPropertiesConstraint.php new file mode 100644 index 000000000..b881a2d53 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaxPropertiesConstraint.php @@ -0,0 +1,39 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'maxProperties')) { + return; + } + + if (!is_object($value)) { + return; + } + + $count = count(get_object_vars($value)); + if ($count <= $schema->maxProperties) { + return; + } + + $this->addError(ConstraintError::PROPERTIES_MAX(), $path, ['maxProperties' => $schema->maxProperties, 'found' => $count]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaximumConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaximumConstraint.php new file mode 100644 index 000000000..bdd1db13c --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MaximumConstraint.php @@ -0,0 +1,38 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'maximum')) { + return; + } + + if (!is_numeric($value)) { + return; + } + + if ($value <= $schema->maximum) { + return; + } + + $this->addError(ConstraintError::MAXIMUM(), $path, ['maximum' => $schema->maximum, 'found' => $value]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinItemsConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinItemsConstraint.php new file mode 100644 index 000000000..b17cd8949 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinItemsConstraint.php @@ -0,0 +1,39 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'minItems')) { + return; + } + + if (!is_array($value)) { + return; + } + + $count = count($value); + if ($count >= $schema->minItems) { + return; + } + + $this->addError(ConstraintError::MIN_ITEMS(), $path, ['minItems' => $schema->minItems, 'found' => $count]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinLengthConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinLengthConstraint.php new file mode 100644 index 000000000..d9c516a33 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinLengthConstraint.php @@ -0,0 +1,39 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'minLength')) { + return; + } + + if (!is_string($value)) { + return; + } + + $length = mb_strlen($value); + if ($length >= $schema->minLength) { + return; + } + + $this->addError(ConstraintError::LENGTH_MIN(), $path, ['minLength' => $schema->minLength, 'found' => $length]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinPropertiesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinPropertiesConstraint.php new file mode 100644 index 000000000..148b4055c --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinPropertiesConstraint.php @@ -0,0 +1,39 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'minProperties')) { + return; + } + + if (!is_object($value)) { + return; + } + + $count = count(get_object_vars($value)); + if ($count >= $schema->minProperties) { + return; + } + + $this->addError(ConstraintError::PROPERTIES_MIN(), $path, ['minProperties' => $schema->minProperties, 'found' => $count]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinimumConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinimumConstraint.php new file mode 100644 index 000000000..b083b8f69 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MinimumConstraint.php @@ -0,0 +1,38 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'minimum')) { + return; + } + + if (!is_numeric($value)) { + return; + } + + if ($value >= $schema->minimum) { + return; + } + + $this->addError(ConstraintError::MINIMUM(), $path, ['minimum' => $schema->minimum, 'found' => $value]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php new file mode 100644 index 000000000..f82440df8 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php @@ -0,0 +1,54 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'multipleOf')) { + return; + } + + if (!is_int($schema->multipleOf) && !is_float($schema->multipleOf) && $schema->multipleOf <= 0.0) { + return; + } + + if (!is_int($value) && !is_float($value)) { + return; + } + + if ($this->isMultipleOf($value, $schema->multipleOf)) { + return; + } + + $this->addError(ConstraintError::MULTIPLE_OF(), $path, ['multipleOf' => $schema->multipleOf, 'found' => $value]); + } + + /** + * @param int|float $number1 + * @param int|float $number2 + */ + private function isMultipleOf($number1, $number2): bool + { + $modulus = ($number1 - round($number1 / $number2) * $number2); + $precision = 0.0000000001; + + return -$precision < $modulus && $modulus < $precision; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/NotConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/NotConstraint.php new file mode 100644 index 000000000..2a8268e16 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/NotConstraint.php @@ -0,0 +1,40 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'not')) { + return; + } + + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($value, $schema->not, $path, $i); + + if (!$schemaConstraint->isValid()) { + return; + } + + $this->addError(ConstraintError::NOT(), $path); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/OneOfConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/OneOfConstraint.php new file mode 100644 index 000000000..cd8efd94e --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/OneOfConstraint.php @@ -0,0 +1,50 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'oneOf')) { + return; + } + + $matchedSchema = 0; + foreach ($schema->oneOf as $oneOfSchema) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($value, $oneOfSchema, $path, $i); + + if ($schemaConstraint->isValid()) { + $matchedSchema++; + continue; + } + + $this->addErrors($schemaConstraint->getErrors()); + } + + if ($matchedSchema !== 1) { + $this->addError(ConstraintError::ONE_OF(), $path); + } else { + $this->errorBag()->reset(); + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternConstraint.php new file mode 100644 index 000000000..5c705f4ae --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternConstraint.php @@ -0,0 +1,63 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'pattern')) { + return; + } + + if (!is_string($value)) { + return; + } + + $matchPattern = $this->createPregMatchPattern($schema->pattern); + if (preg_match($matchPattern, $value) === 1) { + return; + } + + $this->addError(ConstraintError::PATTERN(), $path, ['found' => $value, 'pattern' => $schema->pattern]); + } + + private function createPregMatchPattern(string $pattern): string + { + $replacements = [ + '\D' => '[^0-9]', + '\d' => '[0-9]', + '\p{digit}' => '[0-9]', + '\w' => '[A-Za-z0-9_]', + '\W' => '[^A-Za-z0-9_]', + '\s' => '[\s\x{200B}]', // Explicitly include zero width white space + '\p{Letter}' => '\p{L}', // Map ECMA long property name to PHP (PCRE) Unicode property abbreviations + ]; + + $pattern = str_replace( + array_keys($replacements), + array_values($replacements), + $pattern + ); + + return '/' . str_replace('/', '\/', $pattern) . '/u'; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php new file mode 100644 index 000000000..969736bfb --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php @@ -0,0 +1,72 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'patternProperties')) { + return; + } + + if (!is_object($value)) { + return; + } + + $properties = get_object_vars($value); + + foreach ($properties as $propertyName => $propertyValue) { + foreach ($schema->patternProperties as $patternPropertyRegex => $patternPropertySchema) { + $matchPattern = $this->createPregMatchPattern($patternPropertyRegex); + if (preg_match($matchPattern, (string) $propertyName)) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($propertyValue, $patternPropertySchema, $path, $i); + if ($schemaConstraint->isValid()) { + continue; + } + + $this->addErrors($schemaConstraint->getErrors()); + } + } + } + } + + private function createPregMatchPattern(string $pattern): string + { + $replacements = [ +// '\D' => '[^0-9]', + '\d' => '[0-9]', + '\p{digit}' => '[0-9]', +// '\w' => '[A-Za-z0-9_]', +// '\W' => '[^A-Za-z0-9_]', +// '\s' => '[\s\x{200B}]' // Explicitly include zero width white space + '\p{Letter}' => '\p{L}', // Map ECMA long property name to PHP (PCRE) Unicode property abbreviations + ]; + + $pattern = str_replace( + array_keys($replacements), + array_values($replacements), + $pattern + ); + + return '/' . str_replace('/', '\/', $pattern) . '/u'; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesConstraint.php new file mode 100644 index 000000000..4a9c48081 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesConstraint.php @@ -0,0 +1,48 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'properties')) { + return; + } + + if (!is_object($value)) { + return; + } + + foreach ($schema->properties as $propertyName => $propertySchema) { + $schemaConstraint = $this->factory->createInstanceFor('schema'); + if (!property_exists($value, $propertyName)) { + continue; + } + + $schemaConstraint->check($value->{$propertyName}, $propertySchema, $path, $i); + if ($schemaConstraint->isValid()) { + continue; + } + + $this->addErrors($schemaConstraint->getErrors()); + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesNamesConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesNamesConstraint.php new file mode 100644 index 000000000..d621ecf26 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/PropertiesNamesConstraint.php @@ -0,0 +1,65 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'propertyNames')) { + return; + } + + if (!is_object($value)) { + return; + } + if ($schema->propertyNames === true) { + return; + } + + $propertyNames = get_object_vars($value); + + if ($schema->propertyNames === false) { + foreach ($propertyNames as $propertyName => $_) { + $this->addError(ConstraintError::PROPERTY_NAMES(), $path, ['propertyNames' => $schema->propertyNames, 'violating' => 'false', 'name' => $propertyName]); + } + + return; + } + + if (property_exists($schema->propertyNames, 'maxLength')) { + foreach ($propertyNames as $propertyName => $_) { + $length = mb_strlen($propertyName); + if ($length > $schema->propertyNames->maxLength) { + $this->addError(ConstraintError::PROPERTY_NAMES(), $path, ['propertyNames' => $schema->propertyNames, 'violating' => 'maxLength', 'length' => $length, 'name' => $propertyName]); + } + } + } + + if (property_exists($schema->propertyNames, 'pattern')) { + foreach ($propertyNames as $propertyName => $_) { + if (!preg_match('/' . str_replace('/', '\/', $schema->propertyNames->pattern) . '/', $propertyName)) { + $this->addError(ConstraintError::PROPERTY_NAMES(), $path, ['propertyNames' => $schema->propertyNames, 'violating' => 'pattern', 'name' => $propertyName]); + } + } + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RefConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RefConstraint.php new file mode 100644 index 000000000..f52bcda0a --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RefConstraint.php @@ -0,0 +1,45 @@ +factory = $factory ?: new Factory(); + $this->initialiseErrorBag($this->factory); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, '$ref')) { + return; + } + + try { + $refSchema = $this->factory->getSchemaStorage()->resolveRefSchema($schema); + } catch (\Exception $e) { + return; + } + + $schemaConstraint = $this->factory->createInstanceFor('schema'); + $schemaConstraint->check($value, $refSchema, $path, $i); + + if ($schemaConstraint->isValid()) { + return; + } + + $this->addErrors($schemaConstraint->getErrors()); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php new file mode 100644 index 000000000..9e56ef221 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php @@ -0,0 +1,58 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'required')) { + return; + } + + if (!is_object($value)) { + return; + } + + foreach ($schema->required as $required) { + if (property_exists($value, $required)) { + continue; + } + + $this->addError(ConstraintError::REQUIRED(), $this->incrementPath($path, $required), ['property' => $required]); + } + } + + /** + * @todo refactor as this was only copied from UndefinedConstraint + * Bubble down the path + * + * @param JsonPointer|null $path Current path + * @param mixed $i What to append to the path + */ + protected function incrementPath(?JsonPointer $path, $i): JsonPointer + { + $path = $path ?? new JsonPointer(''); + + if ($i === null || $i === '') { + return $path; + } + + return $path->withPropertyPaths(array_merge($path->getPropertyPaths(), [$i])); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/TypeConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/TypeConstraint.php new file mode 100644 index 000000000..531a4f95b --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/TypeConstraint.php @@ -0,0 +1,50 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'type')) { + return; + } + + $schemaTypes = (array) $schema->type; + $valueType = strtolower(gettype($value)); + // All specific number types are a number + $valueIsNumber = $valueType === 'double' || $valueType === 'integer'; + // A float with zero fractional part is an integer + $isInteger = $valueIsNumber && fmod($value, 1.0) === 0.0; + + foreach ($schemaTypes as $type) { + if ($valueType === $type) { + return; + } + + if ($type === 'number' && $valueIsNumber) { + return; + } + if ($type === 'integer' && $isInteger) { + return; + } + } + + $this->addError(ConstraintError::TYPE(), $path, ['found' => $valueType, 'expected' => implode(', ', $schemaTypes)]); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/UniqueItemsConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/UniqueItemsConstraint.php new file mode 100644 index 000000000..93453401a --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Drafts/Draft06/UniqueItemsConstraint.php @@ -0,0 +1,48 @@ +initialiseErrorBag($factory ?: new Factory()); + } + + public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void + { + if (!property_exists($schema, 'uniqueItems')) { + return; + } + if (!is_array($value)) { + return; + } + + if ($schema->uniqueItems !== true) { + // If unique items not is true duplicates are allowed. + return; + } + + $count = count($value); + for ($x = 0; $x < $count - 1; $x++) { + for ($y = $x + 1; $y < $count; $y++) { + if (DeepComparer::isEqual($value[$x], $value[$y])) { + $this->addError(ConstraintError::UNIQUE_ITEMS(), $path); + + return; + } + } + } + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php index b9220b9d8..5d7e67d08 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php @@ -11,6 +11,7 @@ namespace JsonSchema\Constraints; +use JsonSchema\DraftIdentifiers; use JsonSchema\Exception\InvalidArgumentException; use JsonSchema\SchemaStorage; use JsonSchema\SchemaStorageInterface; @@ -46,10 +47,17 @@ class Factory private $typeCheck = []; /** - * @var int Validation context + * @var int-mask-of Validation context */ protected $errorContext = Validator::ERROR_DOCUMENT_VALIDATION; + /** + * The default dialect used for strict mode (Constraint::CHECK_MODE_STRICT) when the schema is without a schema property + * + * @var string + */ + private $defaultDialect = DraftIdentifiers::DRAFT_6; + /** * @var array */ @@ -65,7 +73,8 @@ class Factory 'const' => 'JsonSchema\Constraints\ConstConstraint', 'format' => 'JsonSchema\Constraints\FormatConstraint', 'schema' => 'JsonSchema\Constraints\SchemaConstraint', - 'validator' => 'JsonSchema\Validator' + 'validator' => 'JsonSchema\Validator', + 'draft06' => Drafts\Draft06\Draft06Constraint::class, ]; /** @@ -198,7 +207,7 @@ public function createInstanceFor($constraintName) /** * Get the error context * - * @phpstan-return Validator::ERROR_DOCUMENT_VALIDATION|Validator::ERROR_SCHEMA_VALIDATION + * @return int-mask-of */ public function getErrorContext(): int { @@ -208,10 +217,20 @@ public function getErrorContext(): int /** * Set the error context * - * @phpstan-param Validator::ERROR_DOCUMENT_VALIDATION|Validator::ERROR_SCHEMA_VALIDATION $errorContext + * @param int-mask-of $errorContext */ public function setErrorContext(int $errorContext): void { $this->errorContext = $errorContext; } + + public function getDefaultDialect(): string + { + return $this->defaultDialect; + } + + public function setDefaultDialect(string $defaultDialect): void + { + $this->defaultDialect = $defaultDialect; + } } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php index 7852e8518..28d15da91 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php @@ -12,6 +12,7 @@ namespace JsonSchema\Constraints; use JsonSchema\ConstraintError; +use JsonSchema\DraftIdentifiers; use JsonSchema\Entity\JsonPointer; use JsonSchema\Exception\InvalidArgumentException; use JsonSchema\Exception\InvalidSchemaException; @@ -26,7 +27,7 @@ */ class SchemaConstraint extends Constraint { - private const DEFAULT_SCHEMA_SPEC = 'http://json-schema.org/draft-04/schema#'; + private const DEFAULT_SCHEMA_SPEC = DraftIdentifiers::DRAFT_4; /** * {@inheritdoc} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php index 1ea0a7bc1..bb0946d8e 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -239,12 +239,19 @@ protected function applyDefaultValues(&$value, $schema, $path): void return; } + if (is_bool($schema)) { + return; + } + // apply defaults if appropriate $requiredOnly = (bool) $this->factory->getConfig(self::CHECK_MODE_ONLY_REQUIRED_DEFAULTS); if (isset($schema->properties) && LooseTypeCheck::isObject($value)) { // $value is an object or assoc array, and properties are defined - treat as an object foreach ($schema->properties as $currentProperty => $propertyDefinition) { $propertyDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($propertyDefinition); + if (is_bool($propertyDefinition)) { + continue; + } if ( !LooseTypeCheck::propertyExists($value, $currentProperty) && property_exists($propertyDefinition, 'default') @@ -269,6 +276,10 @@ protected function applyDefaultValues(&$value, $schema, $path): void // $value is an array, and items are defined - treat as plain array foreach ($items as $currentItem => $itemDefinition) { $itemDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($itemDefinition); + if (is_bool($itemDefinition)) { + continue; + } + if ( !array_key_exists($currentItem, $value) && property_exists($itemDefinition, 'default') diff --git a/justinrainbow/json-schema/src/JsonSchema/DraftIdentifiers.php b/justinrainbow/json-schema/src/JsonSchema/DraftIdentifiers.php new file mode 100644 index 000000000..d0e05e787 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/DraftIdentifiers.php @@ -0,0 +1,48 @@ +getValue()) { + case self::DRAFT_3: + return 'draft03'; + case self::DRAFT_4: + return 'draft04'; + case self::DRAFT_6: + return 'draft06'; + case self::DRAFT_7: + return 'draft07'; + case self::DRAFT_2019_09: + return 'draft2019-09'; + case self::DRAFT_2020_12: + return 'draft2020-12'; + default: + throw new \Exception('Unsupported schema URI: ' . $this->getValue()); + } + } + + public function withoutFragment(): string + { + return rtrim($this->getValue(), '#'); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBag.php b/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBag.php new file mode 100644 index 000000000..e68ece4d6 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBag.php @@ -0,0 +1,109 @@ +}, + * "context": int-mask-of + * } + * @phpstan-type ErrorList list + */ +class ErrorBag +{ + /** @var Factory */ + private $factory; + + /** @var ErrorList */ + private $errors = []; + + /** + * @var int-mask-of All error types that have occurred + */ + protected $errorMask = Validator::ERROR_NONE; + + public function __construct(Factory $factory) + { + $this->factory = $factory; + } + + public function reset(): void + { + $this->errors = []; + $this->errorMask = Validator::ERROR_NONE; + } + + /** @return ErrorList */ + public function getErrors(): array + { + return $this->errors; + } + + /** @param array $more */ + public function addError(ConstraintError $constraint, ?JsonPointer $path = null, array $more = []): void + { + $message = $constraint->getMessage(); + $name = $constraint->getValue(); + /** @var Error $error */ + $error = [ + 'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')), + 'pointer' => ltrim((string) ($path ?: new JsonPointer('')), '#'), + 'message' => ucfirst(vsprintf($message, array_map(static function ($val) { + if (is_scalar($val)) { + return is_bool($val) ? var_export($val, true) : $val; + } + + return json_encode($val); + }, array_values($more)))), + 'constraint' => [ + 'name' => $name, + 'params' => $more + ], + 'context' => $this->factory->getErrorContext(), + ]; + + if ($this->factory->getConfig(Constraint::CHECK_MODE_EXCEPTIONS)) { + throw new ValidationException(sprintf('Error validating %s: %s', $error['pointer'], $error['message'])); + } + $this->errors[] = $error; + /* @see https://github.com/phpstan/phpstan/issues/9384 */ + $this->errorMask |= $error['context']; // @phpstan-ignore assign.propertyType + } + + /** @param ErrorList $errors */ + public function addErrors(array $errors): void + { + if (!$errors) { + return; + } + + $this->errors = array_merge($this->errors, $errors); + $errorMask = &$this->errorMask; + array_walk($errors, static function ($error) use (&$errorMask) { + $errorMask |= $error['context']; + }); + } + + private function convertJsonPointerIntoPropertyPath(JsonPointer $pointer): string + { + $result = array_map( + static function ($path) { + return sprintf(is_numeric($path) ? '[%d]' : '.%s', $path); + }, + $pointer->getPropertyPaths() + ); + + return trim(implode('', $result), '.'); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBagProxy.php b/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBagProxy.php new file mode 100644 index 000000000..e9d584123 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Entity/ErrorBagProxy.php @@ -0,0 +1,66 @@ +errorBag()->getErrors(); + } + + /** @param ErrorList $errors */ + public function addErrors(array $errors): void + { + $this->errorBag()->addErrors($errors); + } + + /** + * @param array $more more array elements to add to the error + */ + public function addError(ConstraintError $constraint, ?JsonPointer $path = null, array $more = []): void + { + $this->errorBag()->addError($constraint, $path, $more); + } + + public function isValid(): bool + { + return $this->errorBag()->getErrors() === []; + } + + protected function initialiseErrorBag(Factory $factory): ErrorBag + { + if (is_null($this->errorBag)) { + $this->errorBag = new ErrorBag($factory); + } + + return $this->errorBag; + } + + protected function errorBag(): ErrorBag + { + if (is_null($this->errorBag)) { + throw new \RuntimeException('ErrorBag not initialized'); + } + + return $this->errorBag; + } + + public function __clone() + { + $this->errorBag()->reset(); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php b/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php index 3524f681f..2a07060df 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php +++ b/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php @@ -6,27 +6,55 @@ class Rfc3339 { - private const REGEX = '/^(\d{4}-\d{2}-\d{2}[T ]{1}\d{2}:\d{2}:\d{2})(\.\d+)?(Z|([+-]\d{2}):?(\d{2}))$/'; + private const REGEX = '/^(\d{4}-\d{2}-\d{2}[T ](0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):((?:[0-5][0-9]|60)))(\.\d+)?(Z|([+-](0[0-9]|1[0-9]|2[0-3]))(:)?([0-5][0-9]))$/'; /** * Try creating a DateTime instance * - * @param string $string + * @param string $input * * @return \DateTime|null */ - public static function createFromString($string) + public static function createFromString($input): ?\DateTime { - if (!preg_match(self::REGEX, strtoupper($string), $matches)) { + if (!preg_match(self::REGEX, strtoupper($input), $matches)) { return null; } + $input = strtoupper($input); // Cleanup for lowercase t and z + $inputHasTSeparator = strpos($input, 'T'); + $dateAndTime = $matches[1]; - $microseconds = $matches[2] ?: '.000000'; - $timeZone = 'Z' !== $matches[3] ? $matches[4] . ':' . $matches[5] : '+00:00'; - $dateFormat = strpos($dateAndTime, 'T') === false ? 'Y-m-d H:i:s.uP' : 'Y-m-d\TH:i:s.uP'; - $dateTime = \DateTime::createFromFormat($dateFormat, $dateAndTime . $microseconds . $timeZone, new \DateTimeZone('UTC')); + $microseconds = $matches[5] ?: '.000000'; + $timeZone = 'Z' !== $matches[6] ? $matches[6] : '+00:00'; + $dateFormat = $inputHasTSeparator === false ? 'Y-m-d H:i:s.uP' : 'Y-m-d\TH:i:s.uP'; + $dateTime = \DateTimeImmutable::createFromFormat($dateFormat, $dateAndTime . $microseconds . $timeZone, new \DateTimeZone('UTC')); + + if ($dateTime === false) { + return null; + } + + $utcDateTime = $dateTime->setTimezone(new \DateTimeZone('+00:00')); + $oneSecond = new \DateInterval('PT1S'); + + // handle leap seconds + if ($matches[4] === '60' && $utcDateTime->sub($oneSecond)->format('H:i:s') === '23:59:59') { + $dateTime = $dateTime->sub($oneSecond); + $matches[1] = str_replace(':60', ':59', $matches[1]); + } + + // Ensure we still have the same year, month, day, hour, minutes and seconds to ensure no rollover took place. + if ($dateTime->format($inputHasTSeparator ? 'Y-m-d\TH:i:s' : 'Y-m-d H:i:s') !== $matches[1]) { + return null; + } + + $mutable = \DateTime::createFromFormat('U.u', $dateTime->format('U.u')); + if ($mutable === false) { + throw new \RuntimeException('Unable to create DateTime from DateTimeImmutable'); + } + + $mutable->setTimezone($dateTime->getTimezone()); - return $dateTime ?: null; + return $mutable; } } diff --git a/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php b/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php index e7e48ef07..05397e7e2 100644 --- a/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php +++ b/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php @@ -62,9 +62,9 @@ public function addSchema(string $id, $schema = null): void // workaround for bug in draft-03 & draft-04 meta-schemas (id & $ref defined with incorrect format) // see https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/177#issuecomment-293051367 if (is_object($schema) && property_exists($schema, 'id')) { - if ($schema->id === 'http://json-schema.org/draft-04/schema#') { + if ($schema->id === DraftIdentifiers::DRAFT_4) { $schema->properties->id->format = 'uri-reference'; - } elseif ($schema->id === 'http://json-schema.org/draft-03/schema#') { + } elseif ($schema->id === DraftIdentifiers::DRAFT_3) { $schema->properties->id->format = 'uri-reference'; $schema->properties->{'$ref'}->format = 'uri-reference'; } @@ -81,9 +81,10 @@ public function addSchema(string $id, $schema = null): void /** * Recursively resolve all references against the provided base * - * @param mixed $schema + * @param mixed $schema + * @param list $propertyStack */ - private function expandRefs(&$schema, ?string $parentId = null): void + private function expandRefs(&$schema, ?string $parentId = null, array $propertyStack = []): void { if (!is_object($schema)) { if (is_array($schema)) { @@ -100,18 +101,22 @@ private function expandRefs(&$schema, ?string $parentId = null): void $schema->{'$ref'} = (string) $refPointer; } + $parentProperty = array_slice($propertyStack, -1)[0] ?? ''; foreach ($schema as $propertyName => &$member) { - if (in_array($propertyName, ['enum', 'const'])) { + if ($parentProperty !== 'properties' && in_array($propertyName, ['enum', 'const'])) { // Enum and const don't allow $ref as a keyword, see https://github.com/json-schema-org/JSON-Schema-Test-Suite/pull/445 continue; } + $schemaId = $this->findSchemaIdInObject($schema); $childId = $parentId; - if (property_exists($schema, 'id') && is_string($schema->id) && $childId !== $schema->id) { - $childId = $this->uriResolver->resolve($schema->id, $childId); + if (is_string($schemaId) && $childId !== $schemaId) { + $childId = $this->uriResolver->resolve($schemaId, $childId); } - $this->expandRefs($member, $childId); + $clonedPropertyStack = $propertyStack; + $clonedPropertyStack[] = $propertyName; + $this->expandRefs($member, $childId, $clonedPropertyStack); } } @@ -146,6 +151,7 @@ public function resolveRef(string $ref, $resolveStack = []) // get & process the schema $refSchema = $this->getSchema($fileName); foreach ($jsonPointer->getPropertyPaths() as $path) { + $path = urldecode($path); if (is_object($refSchema) && property_exists($refSchema, $path)) { $refSchema = $this->resolveRefSchema($refSchema->{$path}, $resolveStack); } elseif (is_array($refSchema) && array_key_exists($path, $refSchema)) { @@ -179,6 +185,10 @@ public function resolveRefSchema($refSchema, $resolveStack = []) return $this->resolveRef($refSchema->{'$ref'}, $resolveStack); } + if (is_object($refSchema) && array_keys(get_object_vars($refSchema)) === ['']) { + $refSchema = get_object_vars($refSchema)['']; + } + return $refSchema; } @@ -196,17 +206,35 @@ private function scanForSubschemas($schema, string $parentId): void continue; } - if (property_exists($potentialSubSchema, 'id') && is_string($potentialSubSchema->id) && property_exists($potentialSubSchema, 'type')) { + $potentialSubSchemaId = $this->findSchemaIdInObject($potentialSubSchema); + if (is_string($potentialSubSchemaId) && property_exists($potentialSubSchema, 'type')) { // Enum and const don't allow id as a keyword, see https://github.com/json-schema-org/JSON-Schema-Test-Suite/pull/471 if (in_array($propertyName, ['enum', 'const'])) { continue; } + // $id in unknow keywords is not valid + if (in_array($propertyName, [])) { + continue; + } + // Found sub schema - $this->addSchema($this->uriResolver->resolve($potentialSubSchema->id, $parentId), $potentialSubSchema); + $this->addSchema($this->uriResolver->resolve($potentialSubSchemaId, $parentId), $potentialSubSchema); } $this->scanForSubschemas($potentialSubSchema, $parentId); } } + + private function findSchemaIdInObject(object $schema): ?string + { + if (property_exists($schema, 'id') && is_string($schema->id)) { + return $schema->id; + } + if (property_exists($schema, '$id') && is_string($schema->{'$id'})) { + return $schema->{'$id'}; + } + + return null; + } } diff --git a/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php b/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php index f625cdd28..e0aaf2377 100644 --- a/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php +++ b/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php @@ -9,21 +9,21 @@ interface SchemaStorageInterface /** * Adds schema with given identifier * - * @param object $schema + * @param object|bool $schema */ public function addSchema(string $id, $schema = null): void; /** * Returns schema for given identifier, or null if it does not exist * - * @return object + * @return object|bool */ public function getSchema(string $id); /** * Returns schema for given reference with all sub-references resolved * - * @return object + * @return object|bool */ public function resolveRef(string $ref); @@ -32,7 +32,7 @@ public function resolveRef(string $ref); * * @param mixed $refSchema * - * @return object + * @return object|bool */ public function resolveRefSchema($refSchema); } diff --git a/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/RelativeReferenceValidator.php b/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/RelativeReferenceValidator.php index 2409f1441..fd95f7bfd 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/RelativeReferenceValidator.php +++ b/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/RelativeReferenceValidator.php @@ -16,6 +16,10 @@ public static function isValid(string $ref): bool } // Additional checks for invalid cases + if (strpos($ref, '\\') !== false) { + return false; // Backslashes are not allowed in URI references + } + if (preg_match('/^(http|https):\/\//', $ref)) { return false; // Absolute URI } diff --git a/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/UriValidator.php b/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/UriValidator.php index d7ed0a832..178fce918 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/UriValidator.php +++ b/justinrainbow/json-schema/src/JsonSchema/Tool/Validator/UriValidator.php @@ -12,19 +12,23 @@ public static function isValid(string $uri): bool $hierarchicalPattern = '/^ ([a-z][a-z0-9+\-.]*):\/\/ # Scheme (http, https, ftp, etc.) (?:([^:@\/?#]+)(?::([^@\/?#]*))?@)? # Optional userinfo (user:pass@) - ([a-z0-9.-]+|\[[a-f0-9:.]+\]) # Hostname or IPv6 in brackets + ([a-z0-9._~-]+|\[[a-f0-9:.]+\]) # Hostname or IPv6 in brackets (?::(\d{1,5}))? # Optional port (\/[a-zA-Z0-9._~!$&\'()*+,;=:@\/%-]*)* # Path (valid characters only) (\?([^#]*))? # Optional query (\#(.*))? # Optional fragment $/ix'; - // RFC 3986: Non-Hierarchical URIs (mailto, data, urn) + // RFC 3986: Non-Hierarchical URIs (mailto, data, urn, news) $nonHierarchicalPattern = '/^ - (mailto|data|urn): # Only allow known non-hierarchical schemes - (.+) # Must contain at least one character after scheme + (mailto|data|urn|news|tel|file): # Only allow known non-hierarchical schemes + (.+) # Must contain at least one character after scheme $/ix'; + // Validation for newsgroup name (alphanumeric + dots, no empty segments) + $newsGroupPattern = '/^[a-z0-9]+(\.[a-z0-9]+)*$/i'; + $telPattern = '/^\+?[0-9.\-() ]+$/'; // Allows +, digits, separators + // RFC 5322-compliant email validation for `mailto:` URIs $emailPattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'; @@ -40,7 +44,7 @@ public static function isValid(string $uri): bool return false; } - // Validate path (reject illegal characters: < > { } | \ ^ `) + // Validate the path (reject illegal characters: < > { } | \ ^ `) if (!empty($matches[6]) && preg_match('/[<>{}|\\\^`]/', $matches[6])) { return false; } @@ -57,6 +61,14 @@ public static function isValid(string $uri): bool return preg_match($emailPattern, $matches[2]) === 1; } + if ($scheme === 'news') { + return preg_match($newsGroupPattern, $matches[2]) === 1; + } + + if ($scheme === 'tel') { + return preg_match($telPattern, $matches[2]) === 1; + } + return true; // Valid non-hierarchical URI } diff --git a/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php b/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php index 116607aed..311f1d34b 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php +++ b/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php @@ -53,7 +53,9 @@ public function retrieve($uri) $this->fetchMessageBody($response); $this->fetchContentType($response); - curl_close($ch); + if (PHP_VERSION_ID < 80000) { + curl_close($ch); + } return $this->messageBody; } diff --git a/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php b/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php index 18fb7fcf8..f60566930 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php +++ b/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php @@ -29,6 +29,10 @@ class FileGetContents extends AbstractRetriever */ public function retrieve($uri) { + if (function_exists('http_clear_last_response_headers')) { + http_clear_last_response_headers(); + } + $errorMessage = null; set_error_handler(function ($errno, $errstr) use (&$errorMessage) { $errorMessage = $errstr; @@ -51,12 +55,19 @@ public function retrieve($uri) } $this->messageBody = $response; - if (!empty($http_response_header)) { - // $http_response_header cannot be tested, because it's defined in the method's local scope - // See http://php.net/manual/en/reserved.variables.httpresponseheader.php for more info. - $this->fetchContentType($http_response_header); // @codeCoverageIgnore - } else { // @codeCoverageIgnore - // Could be a "file://" url or something else - fake up the response + + if (function_exists('http_get_last_response_headers')) { + // Use http_get_last_response_headers() for compatibility with PHP 8.5+ + // where $http_response_header is deprecated. + $httpResponseHeaders = http_get_last_response_headers(); + } else { + /** @phpstan-ignore nullCoalesce.variable ($http_response_header can non-existing when no http request was done) */ + $httpResponseHeaders = $http_response_header ?? []; + } + + if (!empty($httpResponseHeaders)) { + $this->fetchContentType($httpResponseHeaders); + } else { $this->contentType = null; } @@ -68,7 +79,7 @@ public function retrieve($uri) * * @return bool Whether the Content-Type header was found or not */ - private function fetchContentType(array $headers) + private function fetchContentType(array $headers): bool { foreach (array_reverse($headers) as $header) { if ($this->contentType = self::getContentTypeMatchInHeader($header)) { diff --git a/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php b/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php index 4094cccc3..361512a9f 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php +++ b/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php @@ -31,7 +31,7 @@ class UriRetriever implements BaseUriRetrieverInterface */ protected $translationMap = [ // use local copies of the spec schemas - '|^https?://json-schema.org/draft-(0[34])/schema#?|' => 'package://dist/schema/json-schema-draft-$1.json' + '|^https?://json-schema.org/draft-(0[346])/schema#?|' => 'package://dist/schema/json-schema-draft-$1.json' ]; /** diff --git a/justinrainbow/json-schema/src/JsonSchema/Validator.php b/justinrainbow/json-schema/src/JsonSchema/Validator.php index 0845b0cba..b90f17aa0 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Validator.php +++ b/justinrainbow/json-schema/src/JsonSchema/Validator.php @@ -61,13 +61,35 @@ public function validate(&$value, $schema = null, ?int $checkMode = null): int if (LooseTypeCheck::propertyExists($schema, 'id')) { $schemaURI = LooseTypeCheck::propertyGet($schema, 'id'); } + if (LooseTypeCheck::propertyExists($schema, '$id')) { + $schemaURI = LooseTypeCheck::propertyGet($schema, '$id'); + } $this->factory->getSchemaStorage()->addSchema($schemaURI, $schema); $validator = $this->factory->createInstanceFor('schema'); - $validator->check( - $value, - $this->factory->getSchemaStorage()->getSchema($schemaURI) - ); + $schema = $this->factory->getSchemaStorage()->getSchema($schemaURI); + + // Boolean schema requires no further validation + if (is_bool($schema)) { + if ($schema === false) { + $this->addError(ConstraintError::FALSE()); + } + + return $this->getErrorMask(); + } + + if ($this->factory->getConfig(Constraint::CHECK_MODE_STRICT)) { + $dialect = $this->factory->getDefaultDialect(); + if (property_exists($schema, '$schema')) { + $dialect = $schema->{'$schema'}; + } + + $validator = $this->factory->createInstanceFor( + DraftIdentifiers::byValue($dialect)->toConstraintName() + ); + } + + $validator->check($value, $schema); $this->factory->setConfig($initialCheckMode);