$this->maxSizeBytes) { $fail("The :attribute exceeds the maximum allowed size of {$this->maxSizeBytes} bytes."); return; } // Check structure $keyCount = 0; $depthError = false; $stringError = false; $this->traverseArray($value, 1, $keyCount, $depthError, $stringError); if ($depthError) { $fail("The :attribute exceeds the maximum nesting depth of {$this->maxDepth} levels."); return; } if ($keyCount > $this->maxKeys) { $fail("The :attribute exceeds the maximum of {$this->maxKeys} keys."); return; } if ($stringError) { $fail("The :attribute contains string values exceeding {$this->maxStringLength} characters."); return; } } /** * Recursively traverse array to check depth, key count, and string lengths. */ protected function traverseArray(array $array, int $currentDepth, int &$keyCount, bool &$depthError, bool &$stringError): void { if ($currentDepth > $this->maxDepth) { $depthError = true; return; } foreach ($array as $key => $value) { $keyCount++; if ($keyCount > $this->maxKeys) { return; } if (is_string($value) && strlen($value) > $this->maxStringLength) { $stringError = true; return; } if (is_array($value)) { $this->traverseArray($value, $currentDepth + 1, $keyCount, $depthError, $stringError); if ($depthError || $stringError || $keyCount > $this->maxKeys) { return; } } } } /** * Create with default limits (10KB, 3 depth, 50 keys, 1000 char strings). */ public static function default(): self { return new self; } /** * Create with small payload limits (2KB, 2 depth, 20 keys, 500 char strings). */ public static function small(): self { return new self(2048, 2, 20, 500); } /** * Create with large payload limits (100KB, 5 depth, 200 keys, 5000 char strings). */ public static function large(): self { return new self(102400, 5, 200, 5000); } /** * Create for metadata/tags (5KB, 2 depth, 30 keys, 256 char strings). */ public static function metadata(): self { return new self(5120, 2, 30, 256); } }