From 8a0e084626da546874f97cfccf6398fd7dc0a981 Mon Sep 17 00:00:00 2001 From: Antoine Lelaisant Date: Mon, 3 Oct 2022 13:31:27 +0200 Subject: [PATCH 1/5] fix: json-schema-interface --- src/JsonSchema.php | 115 +++++++++++++----------------------- src/JsonSchemaInterface.php | 21 +++++++ src/Scalar/UuidSchema.php | 2 +- 3 files changed, 64 insertions(+), 74 deletions(-) diff --git a/src/JsonSchema.php b/src/JsonSchema.php index 0227619..550b7a1 100644 --- a/src/JsonSchema.php +++ b/src/JsonSchema.php @@ -6,20 +6,50 @@ /** * @template T of mixed + * + * @implements JsonSchemaInterface */ -abstract class JsonSchema implements JsonSchemaInterface +class JsonSchema implements JsonSchemaInterface { + private function __construct( + protected readonly string $title, + protected readonly string $description, + protected readonly iterable $examples, + protected readonly array $schema + ) { + } + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getExamples(): iterable + { + yield from $this->examples; + } + + public function getSchema(): array + { + return $this->schema; + } + /** - * @param JsonSchema $schema * @template E of mixed + * @param JsonSchemaInterface $schema * * @return JsonSchema */ - public static function nullable(self $schema): self + public static function nullable(JsonSchemaInterface $schema): self { return self::create( - '', - '', + sprintf('Nullable<%s>', $schema->getTitle()), + $schema->getDescription(), [...$schema->getExamples(), null], ['oneOf' => [self::null(), $schema->jsonSerialize()]], ); @@ -36,64 +66,23 @@ public static function create( string $title, string $description, iterable $examples, - $schema + array $schema ): self { - return new class($title, $description, $examples, $schema) extends JsonSchema { - /** - * @var iterable - */ - private iterable $examples; - - /** - * @param iterable $examples - * @param array $schema - */ - public function __construct( - private string $title, - private string $description, - iterable $examples, - private $schema - ) { - $this->examples = [...$examples]; - } - - public function getTitle(): string - { - return $this->title; - } - - public function getDescription(): string - { - return $this->description; - } - - /** - * @return iterable - */ - public function getExamples(): iterable - { - yield from $this->examples; - } - - public function getSchema(): array - { - return $this->schema; - } - }; + return new self($title, $description, $examples, $schema); } /** * @template I * - * @param JsonSchema $jsonSchema + * @param JsonSchemaInterface $jsonSchema * * @return JsonSchema> */ - public static function collection(self $jsonSchema): self + public static function collection(JsonSchemaInterface $jsonSchema): self { return self::create( sprintf('Collection<%s>', $jsonSchema->getTitle()), - '', + $jsonSchema->getDescription(), [[...$jsonSchema->getExamples()]], [ 'type' => 'array', @@ -103,9 +92,9 @@ public static function collection(self $jsonSchema): self } /** - * @return array&array{title: string, description: string, examples: array} + * {@inheritdoc} */ - public function jsonSerialize(): mixed + public function jsonSerialize(): array { $schema = $this->getSchema(); @@ -122,21 +111,6 @@ public function jsonSerialize(): mixed ); } - /** - * @return iterable - */ - abstract public function getExamples(): iterable; - - public function getTitle(): string - { - return ''; - } - - public function getDescription(): string - { - return ''; - } - /** * @param scalar $value * @@ -250,9 +224,4 @@ protected static function oneOf(...$schemas): array 'oneOf' => $schemas, ]; } - - /** - * @return array - */ - abstract protected function getSchema(): array; } diff --git a/src/JsonSchemaInterface.php b/src/JsonSchemaInterface.php index d99c044..87805de 100644 --- a/src/JsonSchemaInterface.php +++ b/src/JsonSchemaInterface.php @@ -6,6 +6,27 @@ use JsonSerializable; +/** + * @template T of mixed + */ interface JsonSchemaInterface extends JsonSerializable { + /** + * @return array&array{title: string, description: string, examples: array} + */ + public function jsonSerialize(): array; + + public function getTitle(): string; + + public function getDescription(): string; + + /** + * @return iterable + */ + public function getExamples(): iterable; + + /** + * @return array + */ + public function getSchema(): array; } diff --git a/src/Scalar/UuidSchema.php b/src/Scalar/UuidSchema.php index b51e083..5aeceb0 100644 --- a/src/Scalar/UuidSchema.php +++ b/src/Scalar/UuidSchema.php @@ -34,7 +34,7 @@ public function getDescription(): string /** * {@inheritDoc} */ - protected function getSchema(): array + public function getSchema(): array { return array_merge( self::string(), From 8de165d6b053e3ee2790b37a321ebf54136eee6b Mon Sep 17 00:00:00 2001 From: Antoine Lelaisant Date: Mon, 3 Oct 2022 13:43:09 +0200 Subject: [PATCH 2/5] fix: getSchema scope in CollectionSchema, EnumSchema and ObjectSchema --- src/CollectionSchema.php | 2 +- src/EnumSchema.php | 2 +- src/ObjectSchema.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CollectionSchema.php b/src/CollectionSchema.php index d73279f..596ede5 100644 --- a/src/CollectionSchema.php +++ b/src/CollectionSchema.php @@ -50,7 +50,7 @@ protected function getRange(): ?int return null; } - protected function getSchema(): array + public function getSchema(): array { $schema = [ 'type' => 'array', diff --git a/src/EnumSchema.php b/src/EnumSchema.php index 2db3285..abedb36 100644 --- a/src/EnumSchema.php +++ b/src/EnumSchema.php @@ -15,7 +15,7 @@ public function getExamples(): iterable return $this->getEnum(); } - protected function getSchema(): array + public function getSchema(): array { return [ 'enum' => [...$this->getEnum()], diff --git a/src/ObjectSchema.php b/src/ObjectSchema.php index 1d8849e..026bbdf 100644 --- a/src/ObjectSchema.php +++ b/src/ObjectSchema.php @@ -59,7 +59,7 @@ protected function addProperty(string $name, JsonSchema $schema, bool $required } } - protected function getSchema(): array + public function getSchema(): array { return [ 'type' => 'object', From 79733f87f7b4857891a7cd3c66a5829b0dae35b7 Mon Sep 17 00:00:00 2001 From: Antoine Lelaisant Date: Thu, 20 Oct 2022 18:13:53 +0200 Subject: [PATCH 3/5] fix: make EnumType, CollectionType and ObjectType implementing JsonSchemaInterface --- src/CollectionSchema.php | 27 ++++++++++++++++++++++++++- src/EnumSchema.php | 26 +++++++++++++++++++++++--- src/ObjectSchema.php | 2 +- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/CollectionSchema.php b/src/CollectionSchema.php index 596ede5..84ee67f 100644 --- a/src/CollectionSchema.php +++ b/src/CollectionSchema.php @@ -11,7 +11,7 @@ * * @extends JsonSchema */ -abstract class CollectionSchema extends JsonSchema +abstract class CollectionSchema implements JsonSchemaInterface { /** * @param JsonSchema $itemSchema @@ -30,6 +30,11 @@ public function getTitle(): string return sprintf('Collection<%s>', $this->itemSchema->getTitle()); } + public function getDescription(): string + { + return sprintf('Collection of %s', $this->itemSchema->getDescription()); + } + protected function getUniqueItems(): ?bool { return null; @@ -71,4 +76,24 @@ public function getSchema(): array return $schema; } + + /** + * {@inheritdoc} + */ + public function jsonSerialize(): array + { + $schema = $this->getSchema(); + + /** + * @var array&array{title: string, description: string, examples: array} + */ + return array_merge( + $schema, + [ + 'title' => $this->getTitle(), + 'description' => $this->getDescription(), + 'examples' => [...$this->getExamples()], + ], + ); + } } diff --git a/src/EnumSchema.php b/src/EnumSchema.php index abedb36..67a5449 100644 --- a/src/EnumSchema.php +++ b/src/EnumSchema.php @@ -8,8 +8,13 @@ * @template E * @extends JsonSchema */ -abstract class EnumSchema extends JsonSchema +abstract class EnumSchema implements JsonSchemaInterface { + /** + * @return iterable + */ + abstract protected function getEnum(): iterable; + public function getExamples(): iterable { return $this->getEnum(); @@ -23,7 +28,22 @@ public function getSchema(): array } /** - * @return iterable + * {@inheritdoc} */ - abstract protected function getEnum(): iterable; + public function jsonSerialize(): array + { + $schema = $this->getSchema(); + + /** + * @var array&array{title: string, description: string, examples: array} + */ + return array_merge( + $schema, + [ + 'title' => $this->getTitle(), + 'description' => $this->getDescription(), + 'examples' => [...$this->getExamples()], + ], + ); + } } diff --git a/src/ObjectSchema.php b/src/ObjectSchema.php index 026bbdf..852c11e 100644 --- a/src/ObjectSchema.php +++ b/src/ObjectSchema.php @@ -8,7 +8,7 @@ * @template T of array * @extends JsonSchema */ -abstract class ObjectSchema extends JsonSchema +abstract class ObjectSchema implements JsonSchemaInterface { /** * @var array> From 0b16fc16a21361aa873ec851f81e58c845e24cf2 Mon Sep 17 00:00:00 2001 From: Antoine Lelaisant Date: Fri, 21 Oct 2022 08:42:48 +0200 Subject: [PATCH 4/5] fix: make ObjectSchema implements JsonSchemaInterface --- src/ObjectSchema.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/ObjectSchema.php b/src/ObjectSchema.php index 852c11e..d419391 100644 --- a/src/ObjectSchema.php +++ b/src/ObjectSchema.php @@ -68,4 +68,24 @@ public function getSchema(): array 'required' => $this->required, ]; } + + /** + * {@inheritdoc} + */ + public function jsonSerialize(): array + { + $schema = $this->getSchema(); + + /** + * @var array&array{title: string, description: string, examples: array} + */ + return array_merge( + $schema, + [ + 'title' => $this->getTitle(), + 'description' => $this->getDescription(), + 'examples' => [...$this->getExamples()], + ], + ); + } } From 61c0b00e3b146baab2237cebd0e98cd28b77ea68 Mon Sep 17 00:00:00 2001 From: Antoine Lelaisant Date: Wed, 26 Oct 2022 11:13:13 +0200 Subject: [PATCH 5/5] fix: JsonSchemaInterface implementations --- src/CollectionSchema.php | 8 ++++---- src/EnumSchema.php | 3 ++- src/JsonSchema.php | 22 ++++++++++++---------- src/JsonSchemaInterface.php | 3 ++- src/ObjectSchema.php | 13 +++++++------ src/Scalar/UuidSchema.php | 27 ++++++++++++++++++++++++--- src/Validator.php | 8 ++++---- 7 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/CollectionSchema.php b/src/CollectionSchema.php index 84ee67f..f6340bc 100644 --- a/src/CollectionSchema.php +++ b/src/CollectionSchema.php @@ -7,16 +7,16 @@ /** * @template I * - * @phpstan-type CollectionSchemaData array + * @type CollectionSchemaData array * - * @extends JsonSchema + * @implements JsonSchemaInterface */ abstract class CollectionSchema implements JsonSchemaInterface { /** - * @param JsonSchema $itemSchema + * @psalm-param JsonSchemaInterface $itemSchema */ - public function __construct(private JsonSchema $itemSchema) + public function __construct(private JsonSchemaInterface $itemSchema) { } diff --git a/src/EnumSchema.php b/src/EnumSchema.php index 67a5449..a310aff 100644 --- a/src/EnumSchema.php +++ b/src/EnumSchema.php @@ -6,7 +6,8 @@ /** * @template E - * @extends JsonSchema + * + * @implements JsonSchemaInterface */ abstract class EnumSchema implements JsonSchemaInterface { diff --git a/src/JsonSchema.php b/src/JsonSchema.php index 550b7a1..e4361d5 100644 --- a/src/JsonSchema.php +++ b/src/JsonSchema.php @@ -4,6 +4,8 @@ namespace Knp\JsonSchema; +use Knp\JsonSchema\Validator\Errors; + /** * @template T of mixed * @@ -116,7 +118,7 @@ public function jsonSerialize(): array * * @return array */ - protected static function constant($value): array + public static function constant($value): array { return [ 'const' => $value, @@ -126,7 +128,7 @@ protected static function constant($value): array /** * @return array */ - protected static function null(): array + public static function null(): array { return [ 'type' => 'null', @@ -136,7 +138,7 @@ protected static function null(): array /** * @return array */ - protected static function text(): array + public static function text(): array { return [ 'type' => 'string', @@ -147,7 +149,7 @@ protected static function text(): array /** * @return array */ - protected static function boolean(): array + public static function boolean(): array { return [ 'type' => 'boolean', @@ -157,7 +159,7 @@ protected static function boolean(): array /** * @return array */ - protected static function string(?string $format = null): array + public static function string(?string $format = null): array { $result = [ ...self::text(), @@ -174,7 +176,7 @@ protected static function string(?string $format = null): array /** * @return array */ - protected static function integer(): array + public static function integer(): array { return [ 'type' => 'integer', @@ -184,7 +186,7 @@ protected static function integer(): array /** * @return array */ - protected static function number(): array + public static function number(): array { return [ 'type' => 'number', @@ -194,7 +196,7 @@ protected static function number(): array /** * @return array */ - protected static function date(): array + public static function date(): array { return [ 'type' => 'string', @@ -205,7 +207,7 @@ protected static function date(): array /** * @return array */ - protected static function positiveInteger(): array + public static function positiveInteger(): array { return [ ...self::integer(), @@ -218,7 +220,7 @@ protected static function positiveInteger(): array * * @return array{oneOf: array>} */ - protected static function oneOf(...$schemas): array + public static function oneOf(...$schemas): array { return [ 'oneOf' => $schemas, diff --git a/src/JsonSchemaInterface.php b/src/JsonSchemaInterface.php index 87805de..039dc34 100644 --- a/src/JsonSchemaInterface.php +++ b/src/JsonSchemaInterface.php @@ -5,9 +5,10 @@ namespace Knp\JsonSchema; use JsonSerializable; +use Knp\JsonSchema\Validator\Errors; /** - * @template T of mixed + * @template T */ interface JsonSchemaInterface extends JsonSerializable { diff --git a/src/ObjectSchema.php b/src/ObjectSchema.php index d419391..979b76c 100644 --- a/src/ObjectSchema.php +++ b/src/ObjectSchema.php @@ -5,13 +5,14 @@ namespace Knp\JsonSchema; /** - * @template T of array - * @extends JsonSchema + * @template T + * + * @implements JsonSchemaInterface */ abstract class ObjectSchema implements JsonSchemaInterface { /** - * @var array> + * @var array> */ private array $properties = []; @@ -44,11 +45,11 @@ protected function hasAdditionalProperties(): bool } /** - * @template S + * @psalm-template S * - * @param JsonSchema $schema + * @psalm-param JsonSchemaInterface $schema */ - protected function addProperty(string $name, JsonSchema $schema, bool $required = true): void + protected function addProperty(string $name, JsonSchemaInterface $schema, bool $required = true): void { $this->properties[$name] = $schema; diff --git a/src/Scalar/UuidSchema.php b/src/Scalar/UuidSchema.php index 5aeceb0..27c02c8 100644 --- a/src/Scalar/UuidSchema.php +++ b/src/Scalar/UuidSchema.php @@ -5,11 +5,12 @@ namespace Knp\JsonSchema\Scalar; use Knp\JsonSchema\JsonSchema; +use Knp\JsonSchema\JsonSchemaInterface; /** - * @extends JsonSchema + * @implements JsonSchemaInterface */ -final class UuidSchema extends JsonSchema +final class UuidSchema implements JsonSchemaInterface { private const PATTERN = '^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$'; @@ -37,7 +38,7 @@ public function getDescription(): string public function getSchema(): array { return array_merge( - self::string(), + JsonSchema::string(), [ 'pattern' => self::PATTERN, 'minLength' => 36, @@ -45,4 +46,24 @@ public function getSchema(): array ] ); } + + /** + * {@inheritdoc} + */ + public function jsonSerialize(): array + { + $schema = $this->getSchema(); + + /** + * @var array&array{title: string, description: string, examples: array} + */ + return array_merge( + $schema, + [ + 'title' => $this->getTitle(), + 'description' => $this->getDescription(), + 'examples' => [...$this->getExamples()], + ], + ); + } } diff --git a/src/Validator.php b/src/Validator.php index 8d98156..6f364df 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -9,10 +9,10 @@ interface Validator { /** - * @template T + * @template T of array * - * @param T $data - * @param class-string>|JsonSchema $schema + * @param array $data + * @param JsonSchemaInterface $schema */ - public function validate($data, $schema): ?Errors; + public function validate(array $data, JsonSchemaInterface $schema): ?Errors; }