021/json-schema
Advanced tools
| .idea | ||
| vendor |
| { | ||
| "name": "021/json-schema", | ||
| "description": "Object-Oriented JSON Schema Generation", | ||
| "type": "library", | ||
| "license": "MIT", | ||
| "autoload": { | ||
| "psr-4": { | ||
| "O21\\JsonSchema\\": "src/" | ||
| } | ||
| }, | ||
| "authors": [ | ||
| { | ||
| "name": "021-projects", | ||
| "email": "20326979+021-projects@users.noreply.github.com" | ||
| } | ||
| ], | ||
| "require": { | ||
| "php": ">=8.1" | ||
| } | ||
| } |
| MIT License | ||
| Copyright (c) 2024 021-projects | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| # Object-Oriented JSON Schema Generation in PHP | ||
| <p align="center"> | ||
| <a href="https://packagist.org/packages/021/json-schema"><img src="https://img.shields.io/packagist/dt/021/json-schema" alt="Total Downloads"></a> | ||
| <a href="https://packagist.org/packages/021/json-schema"><img src="https://img.shields.io/packagist/v/021/json-schema" alt="Latest Stable Version"></a> | ||
| <a href="https://packagist.org/packages/021/json-schema"><img src="https://img.shields.io/packagist/l/021/json-schema" alt="License"></a> | ||
| </p> | ||
| This library provides a way to generate JSON schemas in PHP using an object-oriented approach. It is inspired by the [JSON Schema](https://json-schema.org/) standard and aims to provide a way to generate JSON schemas in a more readable and maintainable way. | ||
| ## Installation | ||
| ```bash | ||
| composer require 021/json-schema | ||
| ``` | ||
| ## Usage | ||
| ```php | ||
| use O21\JsonSchema\Schema; | ||
| use O21\JsonSchema\Enums\Type; | ||
| use O21\JsonSchema\Enums\Format; | ||
| $schema = new Schema( | ||
| schema: 'http://json-schema.org/draft-07/schema#', | ||
| type: Type::OBJECT, | ||
| properties: [ | ||
| 'name' => new Schema( | ||
| type: Type::STRING, | ||
| minLength: 1, | ||
| maxLength: 255 | ||
| ), | ||
| 'age' => new Schema( | ||
| type: Type::INTEGER, | ||
| minimum: 0, | ||
| maximum: 150 | ||
| ), | ||
| 'addresses' => new Schema( | ||
| type: Type::ARRAY, | ||
| items: new Schema( | ||
| type: Type::OBJECT, | ||
| properties: [ | ||
| 'street' => new Schema( | ||
| type: Type::STRING, | ||
| minLength: 1, | ||
| maxLength: 255 | ||
| ), | ||
| 'city' => new Schema( | ||
| type: Type::STRING, | ||
| minLength: 1, | ||
| maxLength: 255 | ||
| ), | ||
| 'zip' => new Schema( | ||
| type: Type::STRING, | ||
| pattern: '^[0-9]{5}$' | ||
| ) | ||
| ], | ||
| required: ['street', 'city', 'zip'] | ||
| ), | ||
| ), | ||
| 'email' => new Schema( | ||
| type: Type::STRING, | ||
| format: Format::EMAIL | ||
| ), | ||
| 'phone' => new Schema( | ||
| type: Type::STRING, | ||
| pattern: '^\+[0-9]{1,3}\.[0-9]{1,14}$' | ||
| ), | ||
| 'is_active' => new Schema( | ||
| type: Type::BOOLEAN, | ||
| default: true, | ||
| ), | ||
| ] | ||
| ); | ||
| // Convert the schema to JSON | ||
| $json = $schema->toJson(); | ||
| // Convert the schema to an object | ||
| $obj = $schema->toObject(); | ||
| ``` | ||
| ### Transform | ||
| Almost all properties listed on the [JSON Schema reference](https://json-schema.org/understanding-json-schema/reference) are supported, but if some properties are missing in the current version of the library or you want to add your own, you can use the transform method. | ||
| It is called when the `Schema` class is transformed into the `stdClass` class to generate JSON from it: | ||
| ```php | ||
| use O21\JsonSchema\Schema; | ||
| $schema = new Schema( | ||
| transform: function (stdClass $schema): void { | ||
| $schema->foo = 'bar'; | ||
| } | ||
| ); | ||
| ``` | ||
| ## Support Us | ||
| - **Bitcoin**: 1G4U12A7VVVaUrmj4KmNt4C5SaDmCXuW49 | ||
| - **Litecoin**: LXjysogo9AHiNE7AnUm4zjprDzCCWVESai | ||
| - **Ethereum**: 0xd23B42D0A84aB51a264953f1a9c9A393c5Ffe4A1 | ||
| - **Tron**: TWEcfzu2UAPsbotZJh8DrEpvdZGho79jTg |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Format; | ||
| use O21\JsonSchema\Enums\Type; | ||
| use O21\JsonSchema\Schema; | ||
| trait AllTypesConstruct | ||
| { | ||
| protected array $callSetAliases = [ | ||
| 'schema' => 'dialect', | ||
| 'properties' => 'addProps', | ||
| 'patternProperties' => 'patternProps', | ||
| 'additionalProperties' => 'additionalProps', | ||
| 'unevaluatedProperties' => 'unevaluatedProps', | ||
| 'minimum' => 'min', | ||
| 'maximum' => 'max', | ||
| 'exclusiveMinimum' => 'exclusiveMin', | ||
| 'exclusiveMaximum' => 'exclusiveMax', | ||
| ]; | ||
| public function __construct( | ||
| // Dialect | ||
| ?string $schema = null, | ||
| protected ?Type $type = null, | ||
| // GenericKeywords | ||
| ?string $title = null, | ||
| ?string $description = null, | ||
| mixed $default = null, | ||
| ?array $examples = null, | ||
| ?bool $deprecated = null, | ||
| ?bool $readOnly = null, | ||
| ?bool $writeOnly = null, | ||
| ?string $comment = null, | ||
| ?array $enum = null, | ||
| mixed $const = null, | ||
| // Composition | ||
| ?array $allOf = null, | ||
| ?array $anyOf = null, | ||
| ?array $oneOf = null, | ||
| ?Schema $not = null, | ||
| // ArrayTypeSchema | ||
| ?array $prefixItems = null, | ||
| Schema|bool|null $items = null, | ||
| ?Schema $contains = null, | ||
| ?int $minContains = null, | ||
| ?int $maxContains = null, | ||
| ?int $minItems = null, | ||
| ?int $maxItems = null, | ||
| ?bool $uniqueItems = null, | ||
| // StringTypeSchema | ||
| ?int $minLength = null, | ||
| ?int $maxLength = null, | ||
| ?Format $format = null, | ||
| ?string $pattern = null, | ||
| // NumericTypeSchema | ||
| ?int $minimum = null, | ||
| ?int $maximum = null, | ||
| int|bool|null $exclusiveMinimum = null, | ||
| int|bool|null $exclusiveMaximum = null, | ||
| ?int $multipleOf = null, | ||
| // ObjectTypeSchema | ||
| ?array $properties = null, | ||
| ?array $required = null, | ||
| ?array $patternProperties = null, | ||
| array|bool|null $additionalProperties = null, | ||
| array|bool|null $unevaluatedProperties = null, | ||
| ?Schema $propertyNames = null, | ||
| ?int $minProperties = null, | ||
| ?int $maxProperties = null, | ||
| // Conditions | ||
| ?array $dependentRequired = null, | ||
| ?array $dependentSchemas = null, | ||
| ?array $if = null, | ||
| ?array $then = null, | ||
| ?array $else = null, | ||
| // Media | ||
| ?string $contentMediaType = null, | ||
| ?string $contentEncoding = null, | ||
| ?Schema $contentSchema = null, | ||
| // Bundling | ||
| protected ?string $id = null, | ||
| protected ?string $anchor = null, | ||
| protected ?string $ref = null, | ||
| protected ?array $defs = null, | ||
| // Transformation | ||
| protected mixed $transform = null, | ||
| ) { | ||
| $args = get_defined_vars(); | ||
| unset($args['this']); | ||
| $args = array_filter($args, static fn($v) => $v !== null); | ||
| foreach ($args as $key => $value) { | ||
| $this->callSet($key, $value); | ||
| } | ||
| } | ||
| protected function callSet(string $key, mixed $value): void | ||
| { | ||
| if ($method = $this->callSetMethod($key)) { | ||
| $this->$method($value); | ||
| } | ||
| } | ||
| protected function callSetMethod(string $key): ?string | ||
| { | ||
| $method = $this->callSetAliases[$key] ?? $key; | ||
| return method_exists($this, $method) ? $method : null; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait ArrayTypeSchema | ||
| { | ||
| protected ?array $_prefixItems = null; | ||
| protected Schema|bool|null $_items = null; | ||
| protected ?Schema $_contains = null; | ||
| protected ?int $_minContains = null; | ||
| protected ?int $_maxContains = null; | ||
| protected ?int $_minItems = null; | ||
| protected ?int $_maxItems = null; | ||
| protected ?bool $_uniqueItems = null; | ||
| protected function arrayObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| if ($this->_prefixItems !== null) { | ||
| $obj->prefixItems = $this->mapToObject($this->_prefixItems); | ||
| } | ||
| if ($this->_items !== null) { | ||
| $obj->items = is_bool($this->_items) | ||
| ? $this->_items | ||
| : $this->_items->toObject(); | ||
| } | ||
| if ($this->_contains !== null) { | ||
| $obj->contains = $this->_contains->toObject(); | ||
| } | ||
| if ($this->_minContains !== null) { | ||
| $obj->minContains = $this->_minContains; | ||
| } | ||
| if ($this->_maxContains !== null) { | ||
| $obj->maxContains = $this->_maxContains; | ||
| } | ||
| if ($this->_minItems !== null) { | ||
| $obj->minItems = $this->_minItems; | ||
| } | ||
| if ($this->_maxItems !== null) { | ||
| $obj->maxItems = $this->_maxItems; | ||
| } | ||
| if ($this->_uniqueItems !== null) { | ||
| $obj->uniqueItems = $this->_uniqueItems; | ||
| } | ||
| return $obj; | ||
| } | ||
| public function prefixItems(array $items): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->assertArrayItemsInstanceOf($items, Schema::class); | ||
| $this->_prefixItems = $items; | ||
| return $this; | ||
| } | ||
| public function items(Schema|bool $items): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_items = $items; | ||
| return $this; | ||
| } | ||
| public function contains(Schema $contains): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_contains = $contains; | ||
| return $this; | ||
| } | ||
| public function minContains(int $minContains): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_minContains = $minContains; | ||
| return $this; | ||
| } | ||
| public function maxContains(int $maxContains): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_maxContains = $maxContains; | ||
| return $this; | ||
| } | ||
| public function minItems(int $minItems): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_minItems = $minItems; | ||
| return $this; | ||
| } | ||
| public function maxItems(int $maxItems): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_maxItems = $maxItems; | ||
| return $this; | ||
| } | ||
| public function uniqueItems(bool $uniqueItems = true): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_uniqueItems = $uniqueItems; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Bundling | ||
| { | ||
| protected ?string $_id = null; | ||
| protected ?string $_anchor = null; | ||
| protected ?string $_ref = null; | ||
| protected ?array $_defs = null; | ||
| public function id(string $id): self | ||
| { | ||
| $this->_id = $id; | ||
| return $this; | ||
| } | ||
| public function anchor(string $anchor): self | ||
| { | ||
| $this->_anchor = $anchor; | ||
| return $this; | ||
| } | ||
| public function ref(string $ref): self | ||
| { | ||
| $this->_ref = $ref; | ||
| return $this; | ||
| } | ||
| public function defs(array $defs): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($defs, Schema::class); | ||
| $this->_defs = $defs; | ||
| return $this; | ||
| } | ||
| protected function applyBundling(\stdClass $schema): void | ||
| { | ||
| if ($this->_id) { | ||
| $schema->{'$id'} = $this->_id; | ||
| } | ||
| if ($this->_anchor) { | ||
| $schema->{'$anchor'} = $this->_anchor; | ||
| } | ||
| if ($this->_ref) { | ||
| $schema->{'$ref'} = $this->_ref; | ||
| } | ||
| if ($this->_defs) { | ||
| $schema->{'$defs'} = $this->arrayToObject($this->_defs); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Composition | ||
| { | ||
| protected array $_allOf = []; | ||
| protected array $_anyOf = []; | ||
| protected array $_oneOf = []; | ||
| protected ?Schema $_not = null; | ||
| public function allOf(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_allOf = $schemas; | ||
| return $this; | ||
| } | ||
| public function anyOf(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_anyOf = $schemas; | ||
| return $this; | ||
| } | ||
| public function oneOf(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_oneOf = $schemas; | ||
| return $this; | ||
| } | ||
| public function not(Schema $schema): self | ||
| { | ||
| $this->_not = $schema; | ||
| return $this; | ||
| } | ||
| protected function applyComposition(\stdClass $schema): void | ||
| { | ||
| if (! empty($this->_allOf)) { | ||
| $schema->allOf = $this->_allOf; | ||
| } | ||
| if (! empty($this->_anyOf)) { | ||
| $schema->anyOf = $this->_anyOf; | ||
| } | ||
| if (! empty($this->_oneOf)) { | ||
| $schema->oneOf = $this->_oneOf; | ||
| } | ||
| if (! is_null($this->_not)) { | ||
| $schema->not = $this->_not->toObject(); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Conditions | ||
| { | ||
| protected array $_dependentRequired = []; | ||
| protected array $_dependentSchemas = []; | ||
| protected array $_if = []; | ||
| protected array $_then = []; | ||
| protected array $_else = []; | ||
| public function dependentRequired(array|string $key, ?array $required = null): self | ||
| { | ||
| if (is_array($key)) { | ||
| foreach ($key as $k => $v) { | ||
| $this->_dependentRequired[$k] = $v; | ||
| } | ||
| return $this; | ||
| } | ||
| $this->_dependentRequired[$key] = $required; | ||
| return $this; | ||
| } | ||
| public function dependentSchemas(array|string $key, ?array $schemas = null): self | ||
| { | ||
| if (is_array($key)) { | ||
| foreach ($key as $k => $v) { | ||
| $this->_dependentSchemas[$k] = $v; | ||
| } | ||
| return $this; | ||
| } | ||
| $this->_dependentSchemas[$key] = $schemas; | ||
| return $this; | ||
| } | ||
| public function if(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_if = $schemas; | ||
| return $this; | ||
| } | ||
| public function then(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_then = $schemas; | ||
| return $this; | ||
| } | ||
| public function else(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_else = $schemas; | ||
| return $this; | ||
| } | ||
| protected function applyConditions(\stdClass $schema): void | ||
| { | ||
| if (! empty($this->_dependentRequired)) { | ||
| $schema->dependentRequired = $this->arrayToObject($this->_dependentRequired); | ||
| } | ||
| if (! empty($this->_dependentSchemas)) { | ||
| $schema->dependentSchemas = $this->arrayToObject($this->_dependentSchemas); | ||
| } | ||
| if (! empty($this->_if)) { | ||
| $schema->if = $this->mapToObject($this->_if); | ||
| } | ||
| if (! empty($this->_then)) { | ||
| $schema->then = $this->mapToObject($this->_then); | ||
| } | ||
| if (! empty($this->_else)) { | ||
| $schema->else = $this->mapToObject($this->_else); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait Dialect | ||
| { | ||
| protected ?string $_dialect = null; | ||
| public function dialect(string $dialect): self | ||
| { | ||
| $this->_dialect = $dialect; | ||
| return $this; | ||
| } | ||
| protected function applyDialect(\stdClass $schema): void | ||
| { | ||
| if ($this->_dialect !== null) { | ||
| $schema->{'$schema'} = $this->_dialect; | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait GenericKeywords | ||
| { | ||
| protected ?string $_title = null; | ||
| protected ?string $_description = null; | ||
| protected mixed $_default = null; | ||
| protected ?array $_examples = null; | ||
| protected ?bool $_deprecated = null; | ||
| protected ?bool $_readOnly = null; | ||
| protected ?bool $_writeOnly = null; | ||
| protected ?string $_comment = null; | ||
| protected ?array $_enum = null; | ||
| protected mixed $_const = null; | ||
| public function title(string $title): static | ||
| { | ||
| $this->_title = $title; | ||
| return $this; | ||
| } | ||
| public function description(string $description): static | ||
| { | ||
| $this->_description = $description; | ||
| return $this; | ||
| } | ||
| public function default(mixed $default): static | ||
| { | ||
| $this->_default = $default; | ||
| return $this; | ||
| } | ||
| public function examples(array $examples): static | ||
| { | ||
| $this->_examples = $examples; | ||
| return $this; | ||
| } | ||
| public function deprecated(bool $deprecated = true): static | ||
| { | ||
| $this->_deprecated = $deprecated; | ||
| return $this; | ||
| } | ||
| public function readOnly(bool $readOnly = true): static | ||
| { | ||
| $this->_readOnly = $readOnly; | ||
| return $this; | ||
| } | ||
| public function writeOnly(bool $writeOnly = true): static | ||
| { | ||
| $this->_writeOnly = $writeOnly; | ||
| return $this; | ||
| } | ||
| public function comment(string $comment): static | ||
| { | ||
| $this->_comment = $comment; | ||
| return $this; | ||
| } | ||
| public function enum(array $enum): static | ||
| { | ||
| $this->_enum = $enum; | ||
| return $this; | ||
| } | ||
| public function const(mixed $const): static | ||
| { | ||
| $this->_const = $const; | ||
| return $this; | ||
| } | ||
| protected function applyGenericKeywords(\stdClass $schema): void | ||
| { | ||
| if ($this->_title !== null) { | ||
| $schema->title = $this->_title; | ||
| } | ||
| if ($this->_description !== null) { | ||
| $schema->description = $this->_description; | ||
| } | ||
| if ($this->_default !== null) { | ||
| $schema->default = $this->_default; | ||
| } | ||
| if ($this->_examples !== null) { | ||
| $schema->examples = $this->_examples; | ||
| } | ||
| if ($this->_deprecated !== null) { | ||
| $schema->deprecated = $this->_deprecated; | ||
| } | ||
| if ($this->_readOnly !== null) { | ||
| $schema->readOnly = $this->_readOnly; | ||
| } | ||
| if ($this->_writeOnly !== null) { | ||
| $schema->writeOnly = $this->_writeOnly; | ||
| } | ||
| if ($this->_comment !== null) { | ||
| $schema->{'$comment'} = $this->_comment; | ||
| } | ||
| if ($this->_enum !== null) { | ||
| $schema->enum = $this->_enum; | ||
| } | ||
| if ($this->_const !== null) { | ||
| $schema->const = $this->_const; | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Media | ||
| { | ||
| protected ?string $_contentEncoding = null; | ||
| protected ?string $_contentMediaType = null; | ||
| protected ?Schema $_contentSchema = null; | ||
| public function contentEncoding(string $contentEncoding): self | ||
| { | ||
| $this->_contentEncoding = $contentEncoding; | ||
| return $this; | ||
| } | ||
| public function contentMediaType(string $contentMediaType): self | ||
| { | ||
| $this->_contentMediaType = $contentMediaType; | ||
| return $this; | ||
| } | ||
| public function contentSchema(Schema $contentSchema): self | ||
| { | ||
| $this->_contentSchema = $contentSchema; | ||
| return $this; | ||
| } | ||
| protected function applyMedia(\stdClass $schema): void | ||
| { | ||
| if ($this->_contentEncoding !== null) { | ||
| $schema->contentEncoding = $this->_contentEncoding; | ||
| } | ||
| if ($this->_contentMediaType !== null) { | ||
| $schema->contentMediaType = $this->_contentMediaType; | ||
| } | ||
| if ($this->_contentSchema !== null) { | ||
| $schema->contentSchema = $this->_contentSchema->toObject(); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait NumericTypeSchema | ||
| { | ||
| protected int|float|null $minimum = null; | ||
| protected int|float|null $maximum = null; | ||
| protected int|float|bool|null $exclusiveMinimum = null; | ||
| protected int|float|bool|null $exclusiveMaximum = null; | ||
| protected int|float|null $_multipleOf = null; | ||
| protected function numericObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| $props = $this->filterArray([ | ||
| 'minimum' => $this->minimum, | ||
| 'maximum' => $this->maximum, | ||
| 'exclusiveMinimum' => $this->exclusiveMinimum, | ||
| 'exclusiveMaximum' => $this->exclusiveMaximum, | ||
| 'multipleOf' => $this->_multipleOf, | ||
| ]); | ||
| $this->applyArrayProps($obj, $props); | ||
| return $obj; | ||
| } | ||
| public function min(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->minimum = $value; | ||
| return $this; | ||
| } | ||
| public function max(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->maximum = $value; | ||
| return $this; | ||
| } | ||
| public function exclusiveMin(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->exclusiveMinimum = $value; | ||
| return $this; | ||
| } | ||
| public function exclusiveMax(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->exclusiveMaximum = $value; | ||
| return $this; | ||
| } | ||
| public function multipleOf(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->_multipleOf = $value; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait ObjectTypeSchema | ||
| { | ||
| protected array $_required = []; | ||
| protected array $properties = []; | ||
| protected array $patternProperties = []; | ||
| protected array|bool $additionalProperties = []; | ||
| protected array|bool $unevaluatedProperties = []; | ||
| protected ?Schema $_propertyNames = null; | ||
| protected ?int $_minProperties = null; | ||
| protected ?int $_maxProperties = null; | ||
| protected function objectObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| if (! empty($this->properties)) { | ||
| $obj->properties = $this->arrayToObject($this->properties); | ||
| } | ||
| if (! empty($this->_required)) { | ||
| $obj->required = $this->_required; | ||
| } | ||
| if (! empty($this->patternProperties)) { | ||
| $obj->patternProperties = $this->arrayToObject($this->patternProperties); | ||
| } | ||
| if (! is_array($this->additionalProperties) | ||
| || ! empty($this->additionalProperties) | ||
| ) { | ||
| $obj->additionalProperties = $this->additionalProperties; | ||
| } | ||
| if (! is_array($this->unevaluatedProperties) | ||
| || ! empty($this->unevaluatedProperties) | ||
| ) { | ||
| $obj->unevaluatedProperties = $this->unevaluatedProperties; | ||
| } | ||
| if ($this->_propertyNames !== null) { | ||
| $obj->propertyNames = $this->_propertyNames->toObject(); | ||
| } | ||
| if ($this->_minProperties !== null) { | ||
| $obj->minProperties = $this->_minProperties; | ||
| } | ||
| if ($this->_maxProperties !== null) { | ||
| $obj->maxProperties = $this->_maxProperties; | ||
| } | ||
| return $obj; | ||
| } | ||
| public function addProps(array $props): void | ||
| { | ||
| $this->assertArrayItemsInstanceOf($props, Schema::class); | ||
| foreach ($props as $key => $schema) { | ||
| $this->addProp($key, $schema); | ||
| } | ||
| } | ||
| public function addProp( | ||
| string $key, | ||
| Schema $schema, | ||
| ): self { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->properties[$key] = $schema; | ||
| return $this; | ||
| } | ||
| public function removeProps(array|string ...$keys): void | ||
| { | ||
| if (count($keys) === 1 && is_array($keys[0])) { | ||
| $keys = $keys[0]; | ||
| } | ||
| foreach ($keys as $key) { | ||
| $this->removeProp($key); | ||
| } | ||
| } | ||
| public function removeProp(string $key): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| unset($this->properties[$key]); | ||
| return $this; | ||
| } | ||
| public function required(array|string ...$keys): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| if (count($keys) === 1 && is_array($keys[0])) { | ||
| $keys = $keys[0]; | ||
| } | ||
| $this->_required = array_merge($this->_required, $keys); | ||
| return $this; | ||
| } | ||
| public function notRequired(array|string ...$keys): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| if (count($keys) === 1 && is_array($keys[0])) { | ||
| $keys = $keys[0]; | ||
| } | ||
| $this->_required = array_diff($this->_required, $keys); | ||
| return $this; | ||
| } | ||
| public function minProperties(int $min): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->_minProperties = $min; | ||
| return $this; | ||
| } | ||
| public function maxProperties(int $max): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->_maxProperties = $max; | ||
| return $this; | ||
| } | ||
| public function additionalProps(bool|Schema $props): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->additionalProperties = $props; | ||
| return $this; | ||
| } | ||
| public function unevaluatedProps(bool|Schema $props): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->unevaluatedProperties = $props; | ||
| return $this; | ||
| } | ||
| public function patternProps(array $props): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->assertArrayItemsInstanceOf($props, Schema::class); | ||
| $this->patternProperties = $props; | ||
| return $this; | ||
| } | ||
| public function propertyNames(Schema $schema): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->_propertyNames = $schema; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait PropGetter | ||
| { | ||
| public function getProp(string $prop): mixed | ||
| { | ||
| if (property_exists($this, $prop)) { | ||
| return $this->$prop; | ||
| } | ||
| if (! str_starts_with($prop, '_')) { | ||
| return $this->getProp('_'.$prop); | ||
| } | ||
| return null; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait StdBuilding | ||
| { | ||
| protected function arrayToObject(array $array): \stdClass | ||
| { | ||
| $obj = new \stdClass(); | ||
| foreach ($array as $key => $value) { | ||
| $obj->{$key} = is_a($value, Schema::class) | ||
| ? $value->toObject() | ||
| : $value; | ||
| } | ||
| return $obj; | ||
| } | ||
| protected function applyArrayProps(\stdClass $object, array $props): void | ||
| { | ||
| foreach ($props as $key => $value) { | ||
| $object->{$key} = $value; | ||
| } | ||
| } | ||
| protected function mapToObject(array $array): array | ||
| { | ||
| return array_map(static fn($value) => $value->toObject(), $array); | ||
| } | ||
| protected function filterArray(array $array): array | ||
| { | ||
| return array_filter($array, static fn($value) => ! empty($value)); | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Format; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait StringTypeSchema | ||
| { | ||
| protected ?int $_minLength = null; | ||
| protected ?int $_maxLength = null; | ||
| protected ?Format $_format = null; | ||
| protected ?string $_pattern = null; | ||
| public function stringObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| if ($this->_minLength !== null) { | ||
| $obj->minLength = $this->_minLength; | ||
| } | ||
| if ($this->_maxLength !== null) { | ||
| $obj->maxLength = $this->_maxLength; | ||
| } | ||
| if ($this->_format !== null) { | ||
| $obj->format = $this->_format->value; | ||
| } | ||
| if ($this->_pattern !== null) { | ||
| $obj->pattern = $this->_pattern; | ||
| } | ||
| return $obj; | ||
| } | ||
| public function minLength(int $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_minLength = $value; | ||
| return $this; | ||
| } | ||
| public function maxLength(int $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_maxLength = $value; | ||
| return $this; | ||
| } | ||
| public function format(Format $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_format = $value; | ||
| return $this; | ||
| } | ||
| public function pattern(string $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_pattern = $value; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait Transformation | ||
| { | ||
| protected $_transform = null; | ||
| /** | ||
| * Apply transformation to the Schema when calling toObject | ||
| * | ||
| * @param callable $transform | ||
| * @return \O21\JsonSchema\Concerns\Transformation|\O21\JsonSchema\Schema | ||
| */ | ||
| public function transform(callable $transform): self | ||
| { | ||
| $this->_transform = $transform; | ||
| return $this; | ||
| } | ||
| protected function applyTransform(\stdClass $obj): void | ||
| { | ||
| if ($this->_transform === null) { | ||
| return; | ||
| } | ||
| call_user_func($this->_transform, $obj); | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait Validation | ||
| { | ||
| protected function assertArrayItemsInstanceOf(array $items, string $class): void | ||
| { | ||
| foreach ($items as $item) { | ||
| if (! is_a($item, $class)) { | ||
| throw new \InvalidArgumentException( | ||
| 'Items must be an instance of '.$class | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| protected function assertType(Type ...$type): void | ||
| { | ||
| if (! in_array($this->type, $type, true)) { | ||
| $types = array_map(static fn($t) => $t->value, $type); | ||
| throw new \InvalidArgumentException( | ||
| 'This method can be called only for ' . implode(', ', $types) . ' type' | ||
| ); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Enums; | ||
| enum Format: string | ||
| { | ||
| case DATE = 'date'; | ||
| case TIME = 'time'; | ||
| case DATE_TIME = 'date-time'; | ||
| case EMAIL = 'email'; | ||
| case IDN_EMAIL = 'idn-email'; | ||
| case HOSTNAME = 'hostname'; | ||
| case IDN_HOSTNAME = 'idn-hostname'; | ||
| case IPV4 = 'ipv4'; | ||
| case IPV6 = 'ipv6'; | ||
| case UUID = 'uuid'; | ||
| case URI = 'uri'; | ||
| case URI_REFERENCE = 'uri-reference'; | ||
| case URI_TEMPLATE = 'uri-template'; | ||
| case IRI = 'iri'; | ||
| case IRI_REFERENCE = 'iri-reference'; | ||
| case JSON_POINTER = 'json-pointer'; | ||
| case RELATIVE_JSON_POINTER = 'relative-json-pointer'; | ||
| case REGEX = 'regex'; | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Enums; | ||
| enum Type: string | ||
| { | ||
| case OBJECT = 'object'; | ||
| case ARRAY = 'array'; | ||
| case STRING = 'string'; | ||
| case NUMBER = 'number'; | ||
| case INTEGER = 'integer'; | ||
| case BOOLEAN = 'boolean'; | ||
| case NULL = 'null'; | ||
| } |
| <?php | ||
| namespace O21\JsonSchema; | ||
| use O21\JsonSchema\Concerns\AllTypesConstruct; | ||
| use O21\JsonSchema\Concerns\ArrayTypeSchema; | ||
| use O21\JsonSchema\Concerns\Bundling; | ||
| use O21\JsonSchema\Concerns\Composition; | ||
| use O21\JsonSchema\Concerns\Conditions; | ||
| use O21\JsonSchema\Concerns\Dialect; | ||
| use O21\JsonSchema\Concerns\GenericKeywords; | ||
| use O21\JsonSchema\Concerns\Media; | ||
| use O21\JsonSchema\Concerns\NumericTypeSchema; | ||
| use O21\JsonSchema\Concerns\ObjectTypeSchema; | ||
| use O21\JsonSchema\Concerns\PropGetter; | ||
| use O21\JsonSchema\Concerns\StdBuilding; | ||
| use O21\JsonSchema\Concerns\StringTypeSchema; | ||
| use O21\JsonSchema\Concerns\Transformation; | ||
| use O21\JsonSchema\Concerns\Validation; | ||
| use O21\JsonSchema\Enums\Type; | ||
| /** | ||
| * Class Schema | ||
| * | ||
| * @package O21\JsonSchema | ||
| * @see https://json-schema.org/understanding-json-schema/reference | ||
| */ | ||
| class Schema | ||
| { | ||
| use AllTypesConstruct; | ||
| use ArrayTypeSchema; | ||
| use StringTypeSchema; | ||
| use ObjectTypeSchema; | ||
| use NumericTypeSchema; | ||
| use Bundling; | ||
| use Dialect; | ||
| use GenericKeywords; | ||
| use Composition; | ||
| use Conditions; | ||
| use Media; | ||
| use Validation; | ||
| use Transformation; | ||
| use StdBuilding; | ||
| use PropGetter; | ||
| /** | ||
| * @throws \JsonException | ||
| */ | ||
| public function toJson(): string | ||
| { | ||
| return json_encode($this->toObject(), JSON_THROW_ON_ERROR); | ||
| } | ||
| public function toObject(): \stdClass | ||
| { | ||
| $obj = match ($this->type) { | ||
| Type::ARRAY => $this->arrayObject(), | ||
| Type::STRING => $this->stringObject(), | ||
| Type::OBJECT => $this->objectObject(), | ||
| Type::NUMBER, Type::INTEGER => $this->numericObject(), | ||
| default => $this->defaultObject(), | ||
| }; | ||
| $this->applyDialect($obj); | ||
| $this->applyComposition($obj); | ||
| $this->applyGenericKeywords($obj); | ||
| $this->applyMedia($obj); | ||
| $this->applyConditions($obj); | ||
| $this->applyBundling($obj); | ||
| $this->applyTransform($obj); | ||
| return $obj; | ||
| } | ||
| protected function defaultObject(): \stdClass | ||
| { | ||
| $obj = new \stdClass(); | ||
| if (! empty($this->type)) { | ||
| $obj->type = $this->type->value; | ||
| } | ||
| return $obj; | ||
| } | ||
| } |
| .idea | ||
| vendor |
| { | ||
| "name": "021/json-schema", | ||
| "description": "Object-Oriented JSON Schema Generation", | ||
| "type": "library", | ||
| "license": "MIT", | ||
| "autoload": { | ||
| "psr-4": { | ||
| "O21\\JsonSchema\\": "src/" | ||
| } | ||
| }, | ||
| "authors": [ | ||
| { | ||
| "name": "021-projects", | ||
| "email": "20326979+021-projects@users.noreply.github.com" | ||
| } | ||
| ], | ||
| "require": { | ||
| "php": ">=8.1" | ||
| } | ||
| } |
| MIT License | ||
| Copyright (c) 2024 021-projects | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| # Object-Oriented JSON Schema Generation in PHP | ||
| <p align="center"> | ||
| <a href="https://packagist.org/packages/021/json-schema"><img src="https://img.shields.io/packagist/dt/021/json-schema" alt="Total Downloads"></a> | ||
| <a href="https://packagist.org/packages/021/json-schema"><img src="https://img.shields.io/packagist/v/021/json-schema" alt="Latest Stable Version"></a> | ||
| <a href="https://packagist.org/packages/021/json-schema"><img src="https://img.shields.io/packagist/l/021/json-schema" alt="License"></a> | ||
| </p> | ||
| This library provides a way to generate JSON schemas in PHP using an object-oriented approach. It is inspired by the [JSON Schema](https://json-schema.org/) standard and aims to provide a way to generate JSON schemas in a more readable and maintainable way. | ||
| ## Installation | ||
| ```bash | ||
| composer require 021/json-schema | ||
| ``` | ||
| ## Usage | ||
| ```php | ||
| use O21\JsonSchema\Schema; | ||
| use O21\JsonSchema\Enums\Type; | ||
| use O21\JsonSchema\Enums\Format; | ||
| $schema = new Schema( | ||
| schema: 'http://json-schema.org/draft-07/schema#', | ||
| type: Type::OBJECT, | ||
| properties: [ | ||
| 'name' => new Schema( | ||
| type: Type::STRING, | ||
| minLength: 1, | ||
| maxLength: 255 | ||
| ), | ||
| 'age' => new Schema( | ||
| type: Type::INTEGER, | ||
| minimum: 0, | ||
| maximum: 150 | ||
| ), | ||
| 'addresses' => new Schema( | ||
| type: Type::ARRAY, | ||
| items: new Schema( | ||
| type: Type::OBJECT, | ||
| properties: [ | ||
| 'street' => new Schema( | ||
| type: Type::STRING, | ||
| minLength: 1, | ||
| maxLength: 255 | ||
| ), | ||
| 'city' => new Schema( | ||
| type: Type::STRING, | ||
| minLength: 1, | ||
| maxLength: 255 | ||
| ), | ||
| 'zip' => new Schema( | ||
| type: Type::STRING, | ||
| pattern: '^[0-9]{5}$' | ||
| ) | ||
| ], | ||
| required: ['street', 'city', 'zip'] | ||
| ), | ||
| ), | ||
| 'email' => new Schema( | ||
| type: Type::STRING, | ||
| format: Format::EMAIL | ||
| ), | ||
| 'phone' => new Schema( | ||
| type: Type::STRING, | ||
| pattern: '^\+[0-9]{1,3}\.[0-9]{1,14}$' | ||
| ), | ||
| 'is_active' => new Schema( | ||
| type: Type::BOOLEAN, | ||
| default: true, | ||
| ), | ||
| ] | ||
| ); | ||
| // Convert the schema to JSON | ||
| $json = $schema->toJson(); | ||
| // Convert the schema to an object | ||
| $obj = $schema->toObject(); | ||
| ``` | ||
| ### Transform | ||
| Almost all properties listed on the [JSON Schema reference](https://json-schema.org/understanding-json-schema/reference) are supported, but if some properties are missing in the current version of the library or you want to add your own, you can use the transform method. | ||
| It is called when the `Schema` class is transformed into the `stdClass` class to generate JSON from it: | ||
| ```php | ||
| use O21\JsonSchema\Schema; | ||
| $schema = new Schema( | ||
| transform: function (stdClass $schema): void { | ||
| $schema->foo = 'bar'; | ||
| } | ||
| ); | ||
| ``` |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Format; | ||
| use O21\JsonSchema\Enums\Type; | ||
| use O21\JsonSchema\Schema; | ||
| trait AllTypesConstruct | ||
| { | ||
| protected array $callSetAliases = [ | ||
| 'schema' => 'dialect', | ||
| 'properties' => 'addProps', | ||
| 'patternProperties' => 'patternProps', | ||
| 'additionalProperties' => 'additionalProps', | ||
| 'unevaluatedProperties' => 'unevaluatedProps', | ||
| 'minimum' => 'min', | ||
| 'maximum' => 'max', | ||
| 'exclusiveMinimum' => 'exclusiveMin', | ||
| 'exclusiveMaximum' => 'exclusiveMax', | ||
| ]; | ||
| public function __construct( | ||
| // Dialect | ||
| ?string $schema = null, | ||
| protected ?Type $type = null, | ||
| // GenericKeywords | ||
| ?string $title = null, | ||
| ?string $description = null, | ||
| mixed $default = null, | ||
| ?array $examples = null, | ||
| ?bool $deprecated = null, | ||
| ?bool $readOnly = null, | ||
| ?bool $writeOnly = null, | ||
| ?string $comment = null, | ||
| ?array $enum = null, | ||
| mixed $const = null, | ||
| // Composition | ||
| ?array $allOf = null, | ||
| ?array $anyOf = null, | ||
| ?array $oneOf = null, | ||
| ?Schema $not = null, | ||
| // ArrayTypeSchema | ||
| ?array $prefixItems = null, | ||
| Schema|bool|null $items = null, | ||
| ?Schema $contains = null, | ||
| ?int $minContains = null, | ||
| ?int $maxContains = null, | ||
| ?int $minItems = null, | ||
| ?int $maxItems = null, | ||
| ?bool $uniqueItems = null, | ||
| // StringTypeSchema | ||
| ?int $minLength = null, | ||
| ?int $maxLength = null, | ||
| ?Format $format = null, | ||
| ?string $pattern = null, | ||
| // NumericTypeSchema | ||
| ?int $minimum = null, | ||
| ?int $maximum = null, | ||
| int|bool|null $exclusiveMinimum = null, | ||
| int|bool|null $exclusiveMaximum = null, | ||
| ?int $multipleOf = null, | ||
| // ObjectTypeSchema | ||
| ?array $properties = null, | ||
| ?array $required = null, | ||
| ?array $patternProperties = null, | ||
| array|bool|null $additionalProperties = null, | ||
| array|bool|null $unevaluatedProperties = null, | ||
| ?Schema $propertyNames = null, | ||
| ?int $minProperties = null, | ||
| ?int $maxProperties = null, | ||
| // Conditions | ||
| ?array $dependentRequired = null, | ||
| ?array $dependentSchemas = null, | ||
| ?array $if = null, | ||
| ?array $then = null, | ||
| ?array $else = null, | ||
| // Media | ||
| ?string $contentMediaType = null, | ||
| ?string $contentEncoding = null, | ||
| ?Schema $contentSchema = null, | ||
| // Bundling | ||
| protected ?string $id = null, | ||
| protected ?string $anchor = null, | ||
| protected ?string $ref = null, | ||
| protected ?array $defs = null, | ||
| ) { | ||
| $args = get_defined_vars(); | ||
| unset($args['this']); | ||
| $args = array_filter($args, static fn($v) => $v !== null); | ||
| foreach ($args as $key => $value) { | ||
| $this->callSet($key, $value); | ||
| } | ||
| } | ||
| protected function callSet(string $key, mixed $value): void | ||
| { | ||
| if ($method = $this->callSetMethod($key)) { | ||
| $this->$method($value); | ||
| } | ||
| } | ||
| protected function callSetMethod(string $key): ?string | ||
| { | ||
| $method = $this->callSetAliases[$key] ?? $key; | ||
| return method_exists($this, $method) ? $method : null; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait ArrayTypeSchema | ||
| { | ||
| protected ?array $_prefixItems = null; | ||
| protected Schema|bool|null $_items = null; | ||
| protected ?Schema $_contains = null; | ||
| protected ?int $_minContains = null; | ||
| protected ?int $_maxContains = null; | ||
| protected ?int $_minItems = null; | ||
| protected ?int $_maxItems = null; | ||
| protected ?bool $_uniqueItems = null; | ||
| protected function arrayObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| if ($this->_prefixItems !== null) { | ||
| $obj->prefixItems = $this->mapToObject($this->_prefixItems); | ||
| } | ||
| if ($this->_items !== null) { | ||
| $obj->items = is_bool($this->_items) | ||
| ? $this->_items | ||
| : $this->_items->toObject(); | ||
| } | ||
| if ($this->_contains !== null) { | ||
| $obj->contains = $this->_contains->toObject(); | ||
| } | ||
| if ($this->_minContains !== null) { | ||
| $obj->minContains = $this->_minContains; | ||
| } | ||
| if ($this->_maxContains !== null) { | ||
| $obj->maxContains = $this->_maxContains; | ||
| } | ||
| if ($this->_minItems !== null) { | ||
| $obj->minItems = $this->_minItems; | ||
| } | ||
| if ($this->_maxItems !== null) { | ||
| $obj->maxItems = $this->_maxItems; | ||
| } | ||
| if ($this->_uniqueItems !== null) { | ||
| $obj->uniqueItems = $this->_uniqueItems; | ||
| } | ||
| return $obj; | ||
| } | ||
| public function prefixItems(array $items): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->assertArrayItemsInstanceOf($items, Schema::class); | ||
| $this->_prefixItems = $items; | ||
| return $this; | ||
| } | ||
| public function items(Schema|bool $items): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_items = $items; | ||
| return $this; | ||
| } | ||
| public function contains(Schema $contains): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_contains = $contains; | ||
| return $this; | ||
| } | ||
| public function minContains(int $minContains): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_minContains = $minContains; | ||
| return $this; | ||
| } | ||
| public function maxContains(int $maxContains): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_maxContains = $maxContains; | ||
| return $this; | ||
| } | ||
| public function minItems(int $minItems): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_minItems = $minItems; | ||
| return $this; | ||
| } | ||
| public function maxItems(int $maxItems): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_maxItems = $maxItems; | ||
| return $this; | ||
| } | ||
| public function uniqueItems(bool $uniqueItems = true): self | ||
| { | ||
| $this->assertType(Type::ARRAY); | ||
| $this->_uniqueItems = $uniqueItems; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Bundling | ||
| { | ||
| protected ?string $_id = null; | ||
| protected ?string $_anchor = null; | ||
| protected ?string $_ref = null; | ||
| protected ?array $_defs = null; | ||
| public function id(string $id): self | ||
| { | ||
| $this->_id = $id; | ||
| return $this; | ||
| } | ||
| public function anchor(string $anchor): self | ||
| { | ||
| $this->_anchor = $anchor; | ||
| return $this; | ||
| } | ||
| public function ref(string $ref): self | ||
| { | ||
| $this->_ref = $ref; | ||
| return $this; | ||
| } | ||
| public function defs(array $defs): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($defs, Schema::class); | ||
| $this->_defs = $defs; | ||
| return $this; | ||
| } | ||
| protected function applyBundling(\stdClass $schema): void | ||
| { | ||
| if ($this->_id) { | ||
| $schema->{'$id'} = $this->_id; | ||
| } | ||
| if ($this->_anchor) { | ||
| $schema->{'$anchor'} = $this->_anchor; | ||
| } | ||
| if ($this->_ref) { | ||
| $schema->{'$ref'} = $this->_ref; | ||
| } | ||
| if ($this->_defs) { | ||
| $schema->{'$defs'} = $this->arrayToObject($this->_defs); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Composition | ||
| { | ||
| protected array $_allOf = []; | ||
| protected array $_anyOf = []; | ||
| protected array $_oneOf = []; | ||
| protected ?Schema $_not = null; | ||
| public function allOf(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_allOf = $schemas; | ||
| return $this; | ||
| } | ||
| public function anyOf(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_anyOf = $schemas; | ||
| return $this; | ||
| } | ||
| public function oneOf(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_oneOf = $schemas; | ||
| return $this; | ||
| } | ||
| public function not(Schema $schema): self | ||
| { | ||
| $this->_not = $schema; | ||
| return $this; | ||
| } | ||
| protected function applyComposition(\stdClass $schema): void | ||
| { | ||
| if (! empty($this->_allOf)) { | ||
| $schema->allOf = $this->_allOf; | ||
| } | ||
| if (! empty($this->_anyOf)) { | ||
| $schema->anyOf = $this->_anyOf; | ||
| } | ||
| if (! empty($this->_oneOf)) { | ||
| $schema->oneOf = $this->_oneOf; | ||
| } | ||
| if (! is_null($this->_not)) { | ||
| $schema->not = $this->_not->toObject(); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Conditions | ||
| { | ||
| protected array $_dependentRequired = []; | ||
| protected array $_dependentSchemas = []; | ||
| protected array $_if = []; | ||
| protected array $_then = []; | ||
| protected array $_else = []; | ||
| public function dependentRequired(array|string $key, ?array $required = null): self | ||
| { | ||
| if (is_array($key)) { | ||
| foreach ($key as $k => $v) { | ||
| $this->_dependentRequired[$k] = $v; | ||
| } | ||
| return $this; | ||
| } | ||
| $this->_dependentRequired[$key] = $required; | ||
| return $this; | ||
| } | ||
| public function dependentSchemas(array|string $key, ?array $schemas = null): self | ||
| { | ||
| if (is_array($key)) { | ||
| foreach ($key as $k => $v) { | ||
| $this->_dependentSchemas[$k] = $v; | ||
| } | ||
| return $this; | ||
| } | ||
| $this->_dependentSchemas[$key] = $schemas; | ||
| return $this; | ||
| } | ||
| public function if(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_if = $schemas; | ||
| return $this; | ||
| } | ||
| public function then(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_then = $schemas; | ||
| return $this; | ||
| } | ||
| public function else(array $schemas): self | ||
| { | ||
| $this->assertArrayItemsInstanceOf($schemas, Schema::class); | ||
| $this->_else = $schemas; | ||
| return $this; | ||
| } | ||
| protected function applyConditions(\stdClass $schema): void | ||
| { | ||
| if (! empty($this->_dependentRequired)) { | ||
| $schema->dependentRequired = $this->arrayToObject($this->_dependentRequired); | ||
| } | ||
| if (! empty($this->_dependentSchemas)) { | ||
| $schema->dependentSchemas = $this->arrayToObject($this->_dependentSchemas); | ||
| } | ||
| if (! empty($this->_if)) { | ||
| $schema->if = $this->mapToObject($this->_if); | ||
| } | ||
| if (! empty($this->_then)) { | ||
| $schema->then = $this->mapToObject($this->_then); | ||
| } | ||
| if (! empty($this->_else)) { | ||
| $schema->else = $this->mapToObject($this->_else); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait Dialect | ||
| { | ||
| protected ?string $_dialect = null; | ||
| public function dialect(string $dialect): self | ||
| { | ||
| $this->_dialect = $dialect; | ||
| return $this; | ||
| } | ||
| protected function applyDialect(\stdClass $schema): void | ||
| { | ||
| if ($this->_dialect !== null) { | ||
| $schema->{'$schema'} = $this->_dialect; | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait GenericKeywords | ||
| { | ||
| protected ?string $_title = null; | ||
| protected ?string $_description = null; | ||
| protected mixed $_default = null; | ||
| protected ?array $_examples = null; | ||
| protected ?bool $_deprecated = null; | ||
| protected ?bool $_readOnly = null; | ||
| protected ?bool $_writeOnly = null; | ||
| protected ?string $_comment = null; | ||
| protected ?array $_enum = null; | ||
| protected mixed $_const = null; | ||
| public function title(string $title): static | ||
| { | ||
| $this->_title = $title; | ||
| return $this; | ||
| } | ||
| public function description(string $description): static | ||
| { | ||
| $this->_description = $description; | ||
| return $this; | ||
| } | ||
| public function default(mixed $default): static | ||
| { | ||
| $this->_default = $default; | ||
| return $this; | ||
| } | ||
| public function examples(array $examples): static | ||
| { | ||
| $this->_examples = $examples; | ||
| return $this; | ||
| } | ||
| public function deprecated(bool $deprecated = true): static | ||
| { | ||
| $this->_deprecated = $deprecated; | ||
| return $this; | ||
| } | ||
| public function readOnly(bool $readOnly = true): static | ||
| { | ||
| $this->_readOnly = $readOnly; | ||
| return $this; | ||
| } | ||
| public function writeOnly(bool $writeOnly = true): static | ||
| { | ||
| $this->_writeOnly = $writeOnly; | ||
| return $this; | ||
| } | ||
| public function comment(string $comment): static | ||
| { | ||
| $this->_comment = $comment; | ||
| return $this; | ||
| } | ||
| public function enum(array $enum): static | ||
| { | ||
| $this->_enum = $enum; | ||
| return $this; | ||
| } | ||
| public function const(mixed $const): static | ||
| { | ||
| $this->_const = $const; | ||
| return $this; | ||
| } | ||
| protected function applyGenericKeywords(\stdClass $schema): void | ||
| { | ||
| if ($this->_title !== null) { | ||
| $schema->title = $this->_title; | ||
| } | ||
| if ($this->_description !== null) { | ||
| $schema->description = $this->_description; | ||
| } | ||
| if ($this->_default !== null) { | ||
| $schema->default = $this->_default; | ||
| } | ||
| if ($this->_examples !== null) { | ||
| $schema->examples = $this->_examples; | ||
| } | ||
| if ($this->_deprecated !== null) { | ||
| $schema->deprecated = $this->_deprecated; | ||
| } | ||
| if ($this->_readOnly !== null) { | ||
| $schema->readOnly = $this->_readOnly; | ||
| } | ||
| if ($this->_writeOnly !== null) { | ||
| $schema->writeOnly = $this->_writeOnly; | ||
| } | ||
| if ($this->_comment !== null) { | ||
| $schema->{'$comment'} = $this->_comment; | ||
| } | ||
| if ($this->_enum !== null) { | ||
| $schema->enum = $this->_enum; | ||
| } | ||
| if ($this->_const !== null) { | ||
| $schema->const = $this->_const; | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait Media | ||
| { | ||
| protected ?string $_contentEncoding = null; | ||
| protected ?string $_contentMediaType = null; | ||
| protected ?Schema $_contentSchema = null; | ||
| public function contentEncoding(string $contentEncoding): self | ||
| { | ||
| $this->_contentEncoding = $contentEncoding; | ||
| return $this; | ||
| } | ||
| public function contentMediaType(string $contentMediaType): self | ||
| { | ||
| $this->_contentMediaType = $contentMediaType; | ||
| return $this; | ||
| } | ||
| public function contentSchema(Schema $contentSchema): self | ||
| { | ||
| $this->_contentSchema = $contentSchema; | ||
| return $this; | ||
| } | ||
| protected function applyMedia(\stdClass $schema): void | ||
| { | ||
| if ($this->_contentEncoding !== null) { | ||
| $schema->contentEncoding = $this->_contentEncoding; | ||
| } | ||
| if ($this->_contentMediaType !== null) { | ||
| $schema->contentMediaType = $this->_contentMediaType; | ||
| } | ||
| if ($this->_contentSchema !== null) { | ||
| $schema->contentSchema = $this->_contentSchema->toObject(); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait NumericTypeSchema | ||
| { | ||
| protected int|float|null $minimum = null; | ||
| protected int|float|null $maximum = null; | ||
| protected int|float|bool|null $exclusiveMinimum = null; | ||
| protected int|float|bool|null $exclusiveMaximum = null; | ||
| protected int|float|null $_multipleOf = null; | ||
| protected function numericObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| $props = $this->filterArray([ | ||
| 'minimum' => $this->minimum, | ||
| 'maximum' => $this->maximum, | ||
| 'exclusiveMinimum' => $this->exclusiveMinimum, | ||
| 'exclusiveMaximum' => $this->exclusiveMaximum, | ||
| 'multipleOf' => $this->_multipleOf, | ||
| ]); | ||
| $this->applyArrayProps($obj, $props); | ||
| return $obj; | ||
| } | ||
| public function min(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->minimum = $value; | ||
| return $this; | ||
| } | ||
| public function max(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->maximum = $value; | ||
| return $this; | ||
| } | ||
| public function exclusiveMin(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->exclusiveMinimum = $value; | ||
| return $this; | ||
| } | ||
| public function exclusiveMax(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->exclusiveMaximum = $value; | ||
| return $this; | ||
| } | ||
| public function multipleOf(int|float $value): self | ||
| { | ||
| $this->assertType(Type::NUMBER, Type::INTEGER); | ||
| $this->_multipleOf = $value; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait ObjectTypeSchema | ||
| { | ||
| protected array $_required = []; | ||
| protected array $properties = []; | ||
| protected array $patternProperties = []; | ||
| protected array|bool $additionalProperties = []; | ||
| protected array|bool $unevaluatedProperties = []; | ||
| protected ?Schema $_propertyNames = null; | ||
| protected ?int $_minProperties = null; | ||
| protected ?int $_maxProperties = null; | ||
| protected function objectObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| if (! empty($this->properties)) { | ||
| $obj->properties = $this->arrayToObject($this->properties); | ||
| } | ||
| if (! empty($this->_required)) { | ||
| $obj->required = $this->_required; | ||
| } | ||
| if (! empty($this->patternProperties)) { | ||
| $obj->patternProperties = $this->arrayToObject($this->patternProperties); | ||
| } | ||
| if (! is_array($this->additionalProperties) | ||
| || ! empty($this->additionalProperties) | ||
| ) { | ||
| $obj->additionalProperties = $this->additionalProperties; | ||
| } | ||
| if (! is_array($this->unevaluatedProperties) | ||
| || ! empty($this->unevaluatedProperties) | ||
| ) { | ||
| $obj->unevaluatedProperties = $this->unevaluatedProperties; | ||
| } | ||
| if ($this->_propertyNames !== null) { | ||
| $obj->propertyNames = $this->_propertyNames->toObject(); | ||
| } | ||
| if ($this->_minProperties !== null) { | ||
| $obj->minProperties = $this->_minProperties; | ||
| } | ||
| if ($this->_maxProperties !== null) { | ||
| $obj->maxProperties = $this->_maxProperties; | ||
| } | ||
| return $obj; | ||
| } | ||
| public function addProps(array $props): void | ||
| { | ||
| $this->assertArrayItemsInstanceOf($props, Schema::class); | ||
| foreach ($props as $key => $schema) { | ||
| $this->addProp($key, $schema); | ||
| } | ||
| } | ||
| public function addProp( | ||
| string $key, | ||
| Schema $schema, | ||
| ): self { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->properties[$key] = $schema; | ||
| return $this; | ||
| } | ||
| public function removeProps(array|string ...$keys): void | ||
| { | ||
| if (count($keys) === 1 && is_array($keys[0])) { | ||
| $keys = $keys[0]; | ||
| } | ||
| foreach ($keys as $key) { | ||
| $this->removeProp($key); | ||
| } | ||
| } | ||
| public function removeProp(string $key): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| unset($this->properties[$key]); | ||
| return $this; | ||
| } | ||
| public function required(array|string ...$keys): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| if (count($keys) === 1 && is_array($keys[0])) { | ||
| $keys = $keys[0]; | ||
| } | ||
| $this->_required = array_merge($this->_required, $keys); | ||
| return $this; | ||
| } | ||
| public function notRequired(array|string ...$keys): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| if (count($keys) === 1 && is_array($keys[0])) { | ||
| $keys = $keys[0]; | ||
| } | ||
| $this->_required = array_diff($this->_required, $keys); | ||
| return $this; | ||
| } | ||
| public function minProperties(int $min): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->_minProperties = $min; | ||
| return $this; | ||
| } | ||
| public function maxProperties(int $max): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->_maxProperties = $max; | ||
| return $this; | ||
| } | ||
| public function additionalProps(bool|Schema $props): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->additionalProperties = $props; | ||
| return $this; | ||
| } | ||
| public function unevaluatedProps(bool|Schema $props): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->unevaluatedProperties = $props; | ||
| return $this; | ||
| } | ||
| public function patternProps(array $props): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->assertArrayItemsInstanceOf($props, Schema::class); | ||
| $this->patternProperties = $props; | ||
| return $this; | ||
| } | ||
| public function propertyNames(Schema $schema): self | ||
| { | ||
| $this->assertType(Type::OBJECT); | ||
| $this->_propertyNames = $schema; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait PropGetter | ||
| { | ||
| public function getProp(string $prop): mixed | ||
| { | ||
| if (property_exists($this, $prop)) { | ||
| return $this->$prop; | ||
| } | ||
| if (! str_starts_with($prop, '_')) { | ||
| return $this->getProp('_'.$prop); | ||
| } | ||
| return null; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Schema; | ||
| trait StdBuilding | ||
| { | ||
| protected function arrayToObject(array $array): \stdClass | ||
| { | ||
| $obj = new \stdClass(); | ||
| foreach ($array as $key => $value) { | ||
| $obj->{$key} = is_a($value, Schema::class) | ||
| ? $value->toObject() | ||
| : $value; | ||
| } | ||
| return $obj; | ||
| } | ||
| protected function applyArrayProps(\stdClass $object, array $props): void | ||
| { | ||
| foreach ($props as $key => $value) { | ||
| $object->{$key} = $value; | ||
| } | ||
| } | ||
| protected function mapToObject(array $array): array | ||
| { | ||
| return array_map(static fn($value) => $value->toObject(), $array); | ||
| } | ||
| protected function filterArray(array $array): array | ||
| { | ||
| return array_filter($array, static fn($value) => ! empty($value)); | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Format; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait StringTypeSchema | ||
| { | ||
| protected ?int $_minLength = null; | ||
| protected ?int $_maxLength = null; | ||
| protected ?Format $_format = null; | ||
| protected ?string $_pattern = null; | ||
| public function stringObject(): \stdClass | ||
| { | ||
| $obj = $this->defaultObject(); | ||
| if ($this->_minLength !== null) { | ||
| $obj->minLength = $this->_minLength; | ||
| } | ||
| if ($this->_maxLength !== null) { | ||
| $obj->maxLength = $this->_maxLength; | ||
| } | ||
| if ($this->_format !== null) { | ||
| $obj->format = $this->_format->value; | ||
| } | ||
| if ($this->_pattern !== null) { | ||
| $obj->pattern = $this->_pattern; | ||
| } | ||
| return $obj; | ||
| } | ||
| public function minLength(int $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_minLength = $value; | ||
| return $this; | ||
| } | ||
| public function maxLength(int $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_maxLength = $value; | ||
| return $this; | ||
| } | ||
| public function format(Format $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_format = $value; | ||
| return $this; | ||
| } | ||
| public function pattern(string $value): self | ||
| { | ||
| $this->assertType(Type::STRING); | ||
| $this->_pattern = $value; | ||
| return $this; | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| trait Transformation | ||
| { | ||
| protected $_transform = null; | ||
| /** | ||
| * Apply transformation to the Schema when calling toObject | ||
| * | ||
| * @param callable $transform | ||
| * @return \O21\JsonSchema\Concerns\Transformation|\O21\JsonSchema\Schema | ||
| */ | ||
| public function transform(callable $transform): self | ||
| { | ||
| $this->_transform = $transform; | ||
| return $this; | ||
| } | ||
| protected function applyTransform(\stdClass $obj): void | ||
| { | ||
| if ($this->_transform === null) { | ||
| return; | ||
| } | ||
| call_user_func($this->_transform, $obj); | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Concerns; | ||
| use O21\JsonSchema\Enums\Type; | ||
| trait Validation | ||
| { | ||
| protected function assertArrayItemsInstanceOf(array $items, string $class): void | ||
| { | ||
| foreach ($items as $item) { | ||
| if (! is_a($item, $class)) { | ||
| throw new \InvalidArgumentException( | ||
| 'Items must be an instance of '.$class | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| protected function assertType(Type ...$type): void | ||
| { | ||
| if (! in_array($this->type, $type, true)) { | ||
| $types = array_map(static fn($t) => $t->value, $type); | ||
| throw new \InvalidArgumentException( | ||
| 'This method can be called only for ' . implode(', ', $types) . ' type' | ||
| ); | ||
| } | ||
| } | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Enums; | ||
| enum Format: string | ||
| { | ||
| case DATE = 'date'; | ||
| case TIME = 'time'; | ||
| case DATE_TIME = 'date-time'; | ||
| case EMAIL = 'email'; | ||
| case IDN_EMAIL = 'idn-email'; | ||
| case HOSTNAME = 'hostname'; | ||
| case IDN_HOSTNAME = 'idn-hostname'; | ||
| case IPV4 = 'ipv4'; | ||
| case IPV6 = 'ipv6'; | ||
| case UUID = 'uuid'; | ||
| case URI = 'uri'; | ||
| case URI_REFERENCE = 'uri-reference'; | ||
| case URI_TEMPLATE = 'uri-template'; | ||
| case IRI = 'iri'; | ||
| case IRI_REFERENCE = 'iri-reference'; | ||
| case JSON_POINTER = 'json-pointer'; | ||
| case RELATIVE_JSON_POINTER = 'relative-json-pointer'; | ||
| case REGEX = 'regex'; | ||
| } |
| <?php | ||
| namespace O21\JsonSchema\Enums; | ||
| enum Type: string | ||
| { | ||
| case OBJECT = 'object'; | ||
| case ARRAY = 'array'; | ||
| case STRING = 'string'; | ||
| case NUMBER = 'number'; | ||
| case INTEGER = 'integer'; | ||
| case BOOLEAN = 'boolean'; | ||
| case NULL = 'null'; | ||
| } |
| <?php | ||
| namespace O21\JsonSchema; | ||
| use O21\JsonSchema\Concerns\AllTypesConstruct; | ||
| use O21\JsonSchema\Concerns\ArrayTypeSchema; | ||
| use O21\JsonSchema\Concerns\Bundling; | ||
| use O21\JsonSchema\Concerns\Composition; | ||
| use O21\JsonSchema\Concerns\Conditions; | ||
| use O21\JsonSchema\Concerns\Dialect; | ||
| use O21\JsonSchema\Concerns\GenericKeywords; | ||
| use O21\JsonSchema\Concerns\Media; | ||
| use O21\JsonSchema\Concerns\NumericTypeSchema; | ||
| use O21\JsonSchema\Concerns\ObjectTypeSchema; | ||
| use O21\JsonSchema\Concerns\PropGetter; | ||
| use O21\JsonSchema\Concerns\StdBuilding; | ||
| use O21\JsonSchema\Concerns\StringTypeSchema; | ||
| use O21\JsonSchema\Concerns\Transformation; | ||
| use O21\JsonSchema\Concerns\Validation; | ||
| use O21\JsonSchema\Enums\Type; | ||
| /** | ||
| * Class Schema | ||
| * | ||
| * @package O21\JsonSchema | ||
| * @see https://json-schema.org/understanding-json-schema/reference | ||
| */ | ||
| class Schema | ||
| { | ||
| use AllTypesConstruct; | ||
| use ArrayTypeSchema; | ||
| use StringTypeSchema; | ||
| use ObjectTypeSchema; | ||
| use NumericTypeSchema; | ||
| use Bundling; | ||
| use Dialect; | ||
| use GenericKeywords; | ||
| use Composition; | ||
| use Conditions; | ||
| use Media; | ||
| use Validation; | ||
| use Transformation; | ||
| use StdBuilding; | ||
| use PropGetter; | ||
| /** | ||
| * @throws \JsonException | ||
| */ | ||
| public function toJson(): string | ||
| { | ||
| return json_encode($this->toObject(), JSON_THROW_ON_ERROR); | ||
| } | ||
| public function toObject(): \stdClass | ||
| { | ||
| $obj = match ($this->type) { | ||
| Type::ARRAY => $this->arrayObject(), | ||
| Type::STRING => $this->stringObject(), | ||
| Type::OBJECT => $this->objectObject(), | ||
| Type::NUMBER, Type::INTEGER => $this->numericObject(), | ||
| default => $this->defaultObject(), | ||
| }; | ||
| $this->applyDialect($obj); | ||
| $this->applyComposition($obj); | ||
| $this->applyGenericKeywords($obj); | ||
| $this->applyMedia($obj); | ||
| $this->applyConditions($obj); | ||
| $this->applyBundling($obj); | ||
| $this->applyTransform($obj); | ||
| return $obj; | ||
| } | ||
| protected function defaultObject(): \stdClass | ||
| { | ||
| $obj = new \stdClass(); | ||
| if (! empty($this->type)) { | ||
| $obj->type = $this->type->value; | ||
| } | ||
| return $obj; | ||
| } | ||
| } |