From 5b596169da4ec245f2cfeb2eaa88ab8e9563de73 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Thu, 20 Mar 2025 14:03:50 -0700 Subject: [PATCH 01/11] Update RefResolver.php Amazon JSON Schema for APPAREL apparently needs more than 400 nesting levels to resolve. Code updated to make it easy to adjust the hardcoded nesting level of 200. --- src/RefResolver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/RefResolver.php b/src/RefResolver.php index 0601d75..3dee788 100644 --- a/src/RefResolver.php +++ b/src/RefResolver.php @@ -13,6 +13,7 @@ class RefResolver public $url; /** @var null|RefResolver */ private $rootResolver; + private static int $MAX_DEEP_NESTING = 500; //Change this if you receive DEEP_NESTING exceptions /** * @param mixed $resolutionScope @@ -224,7 +225,7 @@ public function resolveReference($referencePath) */ public function preProcessReferences($data, Context $options, $nestingLevel = 0) { - if ($nestingLevel > 200) { + if ($nestingLevel > self::$MAX_DEEP_NESTING) { //Updated due to specific recursion depth from Amazon product JSON Schemas - yep 200 was not enough throw new Exception('Too deep nesting level', Exception::DEEP_NESTING); } if (is_array($data)) { @@ -264,4 +265,4 @@ public function preProcessReferences($data, Context $options, $nestingLevel = 0) } -} \ No newline at end of file +} From 8e80f9b801bc80fc297153ec03b6db0f5de6fafa Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Thu, 20 Mar 2025 14:18:15 -0700 Subject: [PATCH 02/11] Update RefResolver.php Update to allow submitting of a max nest level depth variable via the Schema object. --- src/RefResolver.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/RefResolver.php b/src/RefResolver.php index 3dee788..5fdedde 100644 --- a/src/RefResolver.php +++ b/src/RefResolver.php @@ -13,7 +13,7 @@ class RefResolver public $url; /** @var null|RefResolver */ private $rootResolver; - private static int $MAX_DEEP_NESTING = 500; //Change this if you receive DEEP_NESTING exceptions + private int $max_deep_nesting = 200; //Change this via options submitted to ::import if needed /** * @param mixed $resolutionScope @@ -103,9 +103,10 @@ public function setupResolutionScope($id, $data) * RefResolver constructor. * @param JsonSchema $rootData */ - public function __construct($rootData = null) + public function __construct($rootData = null, int $max_nest_level = 200) { $this->rootData = $rootData; + $this->max_deep_nesting = $max_nest_level; } public function setRootData($rootData) @@ -225,8 +226,8 @@ public function resolveReference($referencePath) */ public function preProcessReferences($data, Context $options, $nestingLevel = 0) { - if ($nestingLevel > self::$MAX_DEEP_NESTING) { //Updated due to specific recursion depth from Amazon product JSON Schemas - yep 200 was not enough - throw new Exception('Too deep nesting level', Exception::DEEP_NESTING); + if ($nestingLevel > $this->max_deep_nesting) { //Updated due to specific recursion depth from Amazon product JSON Schemas - yep 200 was not enough + throw new Exception('Too deep nesting level. Suggest submitting maxNestLevel via options', Exception::DEEP_NESTING); } if (is_array($data)) { foreach ($data as $key => $item) { From fa77d19cc88e5753114b20cca20ca37293c63a28 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Thu, 20 Mar 2025 14:18:55 -0700 Subject: [PATCH 03/11] Update Schema.php Add ability to submit option to increase max depth of recursion. --- src/Schema.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Schema.php b/src/Schema.php index ff07f0c..8843849 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -165,7 +165,7 @@ public function in($data, ?Context $options = null) $options->import = true; if ($options->refResolver === null) { - $options->refResolver = new RefResolver($data); + $options->refResolver = new RefResolver($data, $options->maxNestLevel ?? 200); } else { $options->refResolver->setRootData($data); } From f90ce82efce29943746375c4bdb63a4fc964a440 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:32:11 -0700 Subject: [PATCH 04/11] Update RefResolver.php Update P0 Critical Finding --- src/RefResolver.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RefResolver.php b/src/RefResolver.php index 5fdedde..b53f1bb 100644 --- a/src/RefResolver.php +++ b/src/RefResolver.php @@ -13,7 +13,7 @@ class RefResolver public $url; /** @var null|RefResolver */ private $rootResolver; - private int $max_deep_nesting = 200; //Change this via options submitted to ::import if needed + private int $maxNestLevel = 200; //Change this via options submitted to ::import if needed /** * @param mixed $resolutionScope @@ -103,10 +103,10 @@ public function setupResolutionScope($id, $data) * RefResolver constructor. * @param JsonSchema $rootData */ - public function __construct($rootData = null, int $max_nest_level = 200) + public function __construct($rootData = null, int $maxNestLevel = 200) { $this->rootData = $rootData; - $this->max_deep_nesting = $max_nest_level; + $this->maxNestLevel = $max_nest_level; } public function setRootData($rootData) From ef31f4854540ba9202a0791625bc296f371d482d Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:33:53 -0700 Subject: [PATCH 05/11] Update Context.php Address P0 Issue by adding $maxNestLevel public property --- src/Context.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Context.php b/src/Context.php index fff2862..d1fd66a 100644 --- a/src/Context.php +++ b/src/Context.php @@ -51,6 +51,9 @@ class Context extends MagicMap public $isRef = false; + /** @var int property max recursive nesting depth */ + public $maxNestLevel = 200; + /** * Dereference $ref unless there is a $ref property defined with format not equal to `uri-reference`. * Default JSON Schema behavior is to dereference only if there is a $ref property defined with format @@ -134,4 +137,4 @@ public function withDefault() } -} \ No newline at end of file +} From 3db4ca8a37f482383d3c933e45570c5f6971271f Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:36:25 -0700 Subject: [PATCH 06/11] Update RefResolver.php Address P1 Issue and improve error messaging when nesting level exceeded. --- src/RefResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RefResolver.php b/src/RefResolver.php index b53f1bb..a7db596 100644 --- a/src/RefResolver.php +++ b/src/RefResolver.php @@ -226,8 +226,8 @@ public function resolveReference($referencePath) */ public function preProcessReferences($data, Context $options, $nestingLevel = 0) { - if ($nestingLevel > $this->max_deep_nesting) { //Updated due to specific recursion depth from Amazon product JSON Schemas - yep 200 was not enough - throw new Exception('Too deep nesting level. Suggest submitting maxNestLevel via options', Exception::DEEP_NESTING); + if ($nestingLevel > $this->maxNestLevel) { //Updated due to specific recursion depth from Amazon product JSON Schemas - yep 200 was not enough + throw new Exception('Too deep nesting level. Nesting / Recursion level (' . $nestingLevel . ') exceeds ' . $this->maxNestLevel , Exception::DEEP_NESTING); } if (is_array($data)) { foreach ($data as $key => $item) { From 5b78fcc562f2a6dbaf407832d6f0576ee68ce824 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:38:11 -0700 Subject: [PATCH 07/11] Update Schema.php Address P1 Issue and set default for max level to constant DEFAULT_MAX_NEST --- src/Schema.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Schema.php b/src/Schema.php index 8843849..f63129b 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -37,6 +37,8 @@ class Schema extends JsonSchema implements MetaHolder, SchemaContract, HasDefaul const CONST_PROPERTY = 'const'; const DEFAULT_PROPERTY = 'default'; + const DEFAULT_MAX_NEST = 200; + const DEFAULT_MAPPING = 'default'; const VERSION_AUTO = 'a'; @@ -165,7 +167,7 @@ public function in($data, ?Context $options = null) $options->import = true; if ($options->refResolver === null) { - $options->refResolver = new RefResolver($data, $options->maxNestLevel ?? 200); + $options->refResolver = new RefResolver($data, $options->maxNestLevel ?? DEFAULT_MAX_NEST); } else { $options->refResolver->setRootData($data); } From 48caf50f32b8cb3679c7691d81f226b0fe8be490 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:38:55 -0700 Subject: [PATCH 08/11] Update Context.php Address P1 Issue and set default for maxNestLevel to constant DEFAULT_MAX_NEST --- src/Context.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Context.php b/src/Context.php index d1fd66a..e0d7917 100644 --- a/src/Context.php +++ b/src/Context.php @@ -4,6 +4,8 @@ class Context extends MagicMap { + const DEFAULT_MAX_NEST = 200; + public $import = true; /** @var DataPreProcessor */ @@ -52,7 +54,7 @@ class Context extends MagicMap public $isRef = false; /** @var int property max recursive nesting depth */ - public $maxNestLevel = 200; + public $maxNestLevel = DEFAULT_MAX_NEST; /** * Dereference $ref unless there is a $ref property defined with format not equal to `uri-reference`. From 46b4199411781b2e304d73254b6f5fecc5b0bc69 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:41:09 -0700 Subject: [PATCH 09/11] Update Schema.php Move constant DEFAULT_MAX_NEST to a static constant from the context object --- src/Schema.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Schema.php b/src/Schema.php index f63129b..e9ffa3b 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -37,8 +37,6 @@ class Schema extends JsonSchema implements MetaHolder, SchemaContract, HasDefaul const CONST_PROPERTY = 'const'; const DEFAULT_PROPERTY = 'default'; - const DEFAULT_MAX_NEST = 200; - const DEFAULT_MAPPING = 'default'; const VERSION_AUTO = 'a'; @@ -167,7 +165,7 @@ public function in($data, ?Context $options = null) $options->import = true; if ($options->refResolver === null) { - $options->refResolver = new RefResolver($data, $options->maxNestLevel ?? DEFAULT_MAX_NEST); + $options->refResolver = new RefResolver($data, $options->maxNestLevel ?? Context::DEFAULT_MAX_NEST); } else { $options->refResolver->setRootData($data); } From 332db60ec1aae072f99d6f632a3939c8af807974 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:41:58 -0700 Subject: [PATCH 10/11] Update Context.php Update constant references --- src/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Context.php b/src/Context.php index e0d7917..c25ef96 100644 --- a/src/Context.php +++ b/src/Context.php @@ -54,7 +54,7 @@ class Context extends MagicMap public $isRef = false; /** @var int property max recursive nesting depth */ - public $maxNestLevel = DEFAULT_MAX_NEST; + public $maxNestLevel = self::DEFAULT_MAX_NEST; /** * Dereference $ref unless there is a $ref property defined with format not equal to `uri-reference`. From 81d5d81a8ca03bbbbd74bcc6de4d27c6278fdf66 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Fri, 21 Mar 2025 10:46:22 -0700 Subject: [PATCH 11/11] Update README.md Update documentation to reflect the new options --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb23097..39cfb8a 100644 --- a/README.md +++ b/README.md @@ -308,7 +308,7 @@ User::export($user); // Exception: Required property missing: id at #->propertie #### Nested structures -Nested structures allow you to make composition: flatten several objects in one and separate back. +Nested structures allow you to make composition: flatten several objects in one and separate back. For nested structures that exceed a depth of the default 200 recursion limit, submit a Context maxNestLevel of an appropriate amount. ```php $user = new User(); @@ -462,7 +462,8 @@ $this->assertSame(4, $res->one); #### Overriding mapping classes If you want to map data to a different class you can register mapping at top level of your importer structure. - +Additionally, you can use the option property, maxNestLevel to increase your depth beyond the default 200 which may be +useful for certain complex Schema's. ```php class CustomSwaggerSchema extends SwaggerSchema { @@ -470,6 +471,7 @@ class CustomSwaggerSchema extends SwaggerSchema { if ($options === null) { $options = new Context(); + $options->maxNestLevel = 500; } $options->objectItemClassMapping[Schema::className()] = CustomSchema::className(); return parent::import($data, $options);