diff --git a/src/Generator.php b/src/Generator.php index a88103d..a6a4a76 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -37,9 +37,9 @@ public function generate(string $namespace, string $destinationPath) } } - private function className(string $className): string + public static function className(string $className): string { - return str_replace(['{', '}', '-', '$', '_'], ['Cb', 'Rcb', 'Dash', '_', '\\'], (new Convert($className))->toPascal()) . ($this->isKeyword($className) ? '_' : ''); + return str_replace(['{', '}', '-', '$', '_'], ['Cb', 'Rcb', 'Dash', '_', '\\'], (new Convert($className))->toPascal()) . (self::isKeyword($className) ? '_' : ''); } private function cleanUpNamespace(string $namespace): string @@ -57,17 +57,17 @@ private function cleanUpNamespace(string $namespace): string */ private function all(string $namespace): iterable { + $schemaRegistry = new SchemaRegistry(); if (count($this->spec->components->schemas ?? []) > 0) { - $schemaClassNameMap = []; foreach ($this->spec->components->schemas as $name => $schema) { - $schemaClassName = $this->className($name); + $schemaClassName = self::className($name); if (strlen($schemaClassName) === 0) { continue; } - $schemaClassNameMap[spl_object_hash($schema)] = $schemaClassName; + $schemaRegistry->addClassName($schemaClassName, $schema); } foreach ($this->spec->components->schemas as $name => $schema) { - $schemaClassName = $schemaClassNameMap[spl_object_hash($schema)]; + $schemaClassName = $schemaRegistry->get($schema); if (strlen($schemaClassName) === 0) { continue; } @@ -77,7 +77,7 @@ private function all(string $namespace): iterable $this->dirname($namespace . 'Schema/' . $schemaClassName), $this->basename($namespace . 'Schema/' . $schemaClassName), $schema, - $schemaClassNameMap, + $schemaRegistry, $namespace . 'Schema' ); } @@ -86,7 +86,7 @@ private function all(string $namespace): iterable $clients = []; if (count($this->spec->paths ?? []) > 0) { foreach ($this->spec->paths as $path => $pathItem) { - $pathClassName = $this->className($path); + $pathClassName = self::className($path); if (strlen($pathClassName) === 0) { continue; } @@ -100,7 +100,7 @@ private function all(string $namespace): iterable ); foreach ($pathItem->getOperations() as $method => $operation) { - $operationClassName = $this->className((new Convert($operation->operationId))->fromTrain()->toPascal()) . '_'; + $operationClassName = self::className((new Convert($operation->operationId))->fromTrain()->toPascal()) . '_'; $operations[$method] = $operationClassName; if (strlen($operationClassName) === 0) { continue; @@ -113,7 +113,7 @@ private function all(string $namespace): iterable $namespace, $this->basename($namespace . 'Operation/' . $operationClassName), $operation, - $schemaClassNameMap + $schemaRegistry ); [$operationGroup, $operationOperation] = explode('/', $operationClassName); @@ -128,7 +128,7 @@ private function all(string $namespace): iterable } } - yield from (function (array $clients, string $namespace, array $schemaClassNameMap): \Generator { + yield from (function (array $clients, string $namespace, SchemaRegistry $schemaRegistry): \Generator { foreach ($clients as $operationGroup => $operations) { yield from Client::generate( $operationGroup, @@ -141,14 +141,14 @@ private function all(string $namespace): iterable yield from Clients::generate( $namespace, $clients, - $schemaClassNameMap, + $schemaRegistry, ); - })($clients, $namespace, $schemaClassNameMap); + })($clients, $namespace, $schemaRegistry); if (count($this->spec->webhooks ?? []) > 0) { $pathClassNameMapping = []; foreach ($this->spec->webhooks as $path => $pathItem) { - $webHookClassName = $this->className($path); + $webHookClassName = self::className($path); $pathClassNameMapping[$path] = $this->fqcn($namespace . 'WebHook/' . $webHookClassName); if (strlen($webHookClassName) === 0) { continue; @@ -160,7 +160,7 @@ private function all(string $namespace): iterable $namespace, $this->basename($namespace . 'WebHook/' . $webHookClassName), $pathItem, - $schemaClassNameMap, + $schemaRegistry, $namespace ); } @@ -175,6 +175,17 @@ private function all(string $namespace): iterable $pathClassNameMapping, ); } + + foreach ($schemaRegistry->unknownSchemas() as $schema) { + yield from Schema::generate( + $schema['name'], + $this->dirname($namespace . 'Schema/' . $schema['className']), + $this->basename($namespace . 'Schema/' . $schema['className']), + $schema['schema'], + $schemaRegistry, + $namespace . 'Schema' + ); + } } private function fqcn(string $fqcn): string @@ -196,7 +207,7 @@ private function basename(string $fqcn): string return $this->cleanUpNamespace(basename($fqcn)); } - private function isKeyword(string $name): bool + private static function isKeyword(string $name): bool { return in_array($name, array('__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor'), false); } diff --git a/src/Generator/Clients.php b/src/Generator/Clients.php index 992bcca..ed76bf4 100644 --- a/src/Generator/Clients.php +++ b/src/Generator/Clients.php @@ -3,6 +3,7 @@ namespace ApiClients\Tools\OpenApiClientGenerator\Generator; use ApiClients\Tools\OpenApiClientGenerator\File; +use ApiClients\Tools\OpenApiClientGenerator\SchemaRegistry; use cebe\openapi\spec\Operation as OpenAPiOperation; use cebe\openapi\spec\PathItem; use Jawira\CaseConverter\Convert; @@ -26,7 +27,7 @@ final class Clients * @return iterable * @throws \Jawira\CaseConverter\CaseConverterException */ - public static function generate(string $namespace, array $clients, array $schemaClassNameMap): iterable + public static function generate(string $namespace, array $clients, SchemaRegistry $schemaRegistry): iterable { $factory = new BuilderFactory(); $stmt = $factory->namespace(rtrim($namespace, '\\')); @@ -94,7 +95,7 @@ public static function generate(string $namespace, array $clients, array $schema $returnType = []; foreach ($operationDetails['operation']->responses as $spec) { foreach ($spec->content as $contentTypeSchema) { - $callReturnTypes[] = $returnType[] = '\\' . $namespace . 'Schema\\' . $schemaClassNameMap[spl_object_hash($contentTypeSchema->schema)]; + $callReturnTypes[] = $returnType[] = '\\' . $namespace . 'Schema\\' . $schemaRegistry->get($contentTypeSchema->schema); } } $operationCalls[] = [ diff --git a/src/Generator/Operation.php b/src/Generator/Operation.php index b3f1be5..d208e12 100644 --- a/src/Generator/Operation.php +++ b/src/Generator/Operation.php @@ -3,6 +3,7 @@ namespace ApiClients\Tools\OpenApiClientGenerator\Generator; use ApiClients\Tools\OpenApiClientGenerator\File; +use ApiClients\Tools\OpenApiClientGenerator\SchemaRegistry; use cebe\openapi\spec\Operation as OpenAPiOperation; use PhpParser\Builder\Param; use PhpParser\BuilderFactory; @@ -25,7 +26,7 @@ final class Operation * @param OpenAPiOperation $operation * @return iterable */ - public static function generate(string $path, string $method, string $namespace, string $rootNamespace, string $className, OpenAPiOperation $operation, array $schemaClassNameMap): iterable + public static function generate(string $path, string $method, string $namespace, string $rootNamespace, string $className, OpenAPiOperation $operation, SchemaRegistry $schemaRegistry): iterable { $factory = new BuilderFactory(); $stmt = $factory->namespace($namespace); @@ -180,7 +181,13 @@ public static function generate(string $path, string $method, string $namespace, new Node\Name('validate'), [ new Node\Arg(new Node\Expr\Variable('data')), - new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [new Node\Scalar\String_(json_encode($requestBodyContent->schema->getSerializableData())), new Node\Scalar\String_('\cebe\openapi\spec\Schema')])), + new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [ + new Node\Expr\ClassConstFetch( + new Node\Name('\\' . $rootNamespace . 'Schema\\' . $schemaRegistry->get($requestBodyContent->schema)), + new Node\Name('SCHEMA_JSON'), + ), + new Node\Scalar\String_('\cebe\openapi\spec\Schema'), + ])), ] )) ); @@ -207,7 +214,7 @@ public static function generate(string $path, string $method, string $namespace, foreach ($operation->responses as $code => $spec) { $contentTypeCases = []; foreach ($spec->content as $contentType => $contentTypeSchema) { - $returnType[] = $object = '\\' . $rootNamespace . 'Schema\\' . $schemaClassNameMap[spl_object_hash($contentTypeSchema->schema)]; + $returnType[] = $object = '\\' . $rootNamespace . 'Schema\\' . $schemaRegistry->get($contentTypeSchema->schema); $ctc = new Node\Stmt\Case_( new Node\Scalar\String_($contentType), [ @@ -219,7 +226,13 @@ public static function generate(string $path, string $method, string $namespace, new Node\Name('validate'), [ new Node\Arg(new Node\Expr\Variable('body')), - new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [new Node\Scalar\String_(json_encode($contentTypeSchema->schema->getSerializableData())), new Node\Scalar\String_('\cebe\openapi\spec\Schema')])), + new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [ + new Node\Expr\ClassConstFetch( + new Node\Name('\\' . $rootNamespace . 'Schema\\' . $schemaRegistry->get($contentTypeSchema->schema)), + new Node\Name('SCHEMA_JSON'), + ), + new Node\Scalar\String_('\cebe\openapi\spec\Schema'), + ])), ] )), new Node\Stmt\Return_(new Node\Expr\MethodCall( diff --git a/src/Generator/Schema.php b/src/Generator/Schema.php index 76faa6e..99194eb 100644 --- a/src/Generator/Schema.php +++ b/src/Generator/Schema.php @@ -3,6 +3,7 @@ namespace ApiClients\Tools\OpenApiClientGenerator\Generator; use ApiClients\Tools\OpenApiClientGenerator\File; +use ApiClients\Tools\OpenApiClientGenerator\SchemaRegistry; use cebe\openapi\spec\Schema as OpenAPiSchema; use Jawira\CaseConverter\Convert; use PhpParser\Builder\Param; @@ -21,12 +22,24 @@ final class Schema * @param OpenAPiSchema $schema * @return iterable */ - public static function generate(string $name, string $namespace, string $className, OpenAPiSchema $schema, array $schemaClassNameMap, string $rootNamespace): iterable + public static function generate(string $name, string $namespace, string $className, OpenAPiSchema $schema, SchemaRegistry $schemaRegistry, string $rootNamespace): iterable { $factory = new BuilderFactory(); $stmt = $factory->namespace($namespace); $class = $factory->class($className)->makeFinal()->addStmt( + new Node\Stmt\ClassConst( + [ + new Node\Const_( + 'SCHEMA_JSON', + new Node\Scalar\String_( + json_encode($schema->getSerializableData()) + ) + ), + ], + Class_::MODIFIER_PUBLIC + ) + )->addStmt( new Node\Stmt\ClassConst( [ new Node\Const_( @@ -53,16 +66,17 @@ public static function generate(string $name, string $namespace, string $classNa ); if ($schema->oneOf !== null && count($schema->oneOf) > 0 && $schema->oneOf[0] instanceof OpenAPiSchema) { - yield from self::fillUpSchema($name, $namespace, $className, $class, $schema->oneOf[0], $factory, $schemaClassNameMap, $rootNamespace); + yield from self::fillUpSchema($name, $namespace, $className, $class, $schema->oneOf[0], $factory, $schemaRegistry, $rootNamespace); } else { - yield from self::fillUpSchema($name, $namespace, $className, $class, $schema, $factory, $schemaClassNameMap, $rootNamespace); + yield from self::fillUpSchema($name, $namespace, $className, $class, $schema, $factory, $schemaRegistry, $rootNamespace); } yield new File($namespace . '\\' . $className, $stmt->addStmt($class)->getNode()); } - private static function fillUpSchema(string $name, string $namespace, string $className, \PhpParser\Builder\Class_ $class, OpenAPiSchema $schema, $factory, array $schemaClassNameMap, string $rootNamespace): iterable + private static function fillUpSchema(string $name, string $namespace, string $className, \PhpParser\Builder\Class_ $class, OpenAPiSchema $schema, $factory, SchemaRegistry $schemaRegistry, string $rootNamespace): iterable { + yield from []; foreach ($schema->properties as $propertyName => $property) { $propertyName = str_replace([ '@', @@ -103,16 +117,16 @@ private static function fillUpSchema(string $name, string $namespace, string $cl } if ($property->type === 'array' && $property->items instanceof OpenAPiSchema) { - if (array_key_exists(spl_object_hash($property->items), $schemaClassNameMap)) { - $methodDocBlock[] = '@return array<\\' . $rootNamespace . '\\' . $schemaClassNameMap[spl_object_hash($property->items)] . '>'; - $propertyDocBlock[] = '@var array<\\' . $rootNamespace . '\\' . $schemaClassNameMap[spl_object_hash($property->items)] . '>'; - $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\HydrateArray(\\' . $rootNamespace . '\\' . $schemaClassNameMap[spl_object_hash($property->items)] . '::class)'; - } elseif ($property->items->type === 'object') { - yield from self::generate($name . '::' . $propertyName, $namespace . '\\' . $className, (new Convert($propertyName))->toPascal(), $property->items, $schemaClassNameMap, $rootNamespace); - $methodDocBlock[] = '@return array<\\' . $namespace . '\\' . $className . '\\' . (new Convert($propertyName))->toPascal() . '>'; - $propertyDocBlock[] = '@var array<\\' . $namespace . '\\' . $className . '\\' . (new Convert($propertyName))->toPascal() . '>'; - $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\HydrateArray(\\' . $namespace . '\\' . $className . '\\' . (new Convert($propertyName))->toPascal() . '::class)'; - } +// if (array_key_exists(spl_object_hash($property->items), $schemaClassNameMap)) { + $methodDocBlock[] = '@return array<\\' . $rootNamespace . '\\' . $schemaRegistry->get($property->items) . '>'; + $propertyDocBlock[] = '@var array<\\' . $rootNamespace . '\\' . $schemaRegistry->get($property->items) . '>'; + $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\HydrateArray(\\' . $rootNamespace . '\\' . $schemaRegistry->get($property->items) . '::class)'; +// } elseif ($property->items->type === 'object') { +// yield from self::generate($name . '::' . $propertyName, $namespace . '\\' . $className, (new Convert($propertyName))->toPascal(), $property->items, $schemaClassNameMap, $rootNamespace); +// $methodDocBlock[] = '@return array<\\' . $namespace . '\\' . $className . '\\' . (new Convert($propertyName))->toPascal() . '>'; +// $propertyDocBlock[] = '@var array<\\' . $namespace . '\\' . $className . '\\' . (new Convert($propertyName))->toPascal() . '>'; +// $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\HydrateArray(\\' . $namespace . '\\' . $className . '\\' . (new Convert($propertyName))->toPascal() . '::class)'; +// } } @@ -140,22 +154,22 @@ private static function fillUpSchema(string $name, string $namespace, string $cl } } - if (is_array($property->anyOf) && $property->anyOf[0] instanceof OpenAPiSchema && array_key_exists(spl_object_hash($property->anyOf[0]), $schemaClassNameMap)) { - $fqcnn = '\\' . $rootNamespace . '\\' . $schemaClassNameMap[spl_object_hash($property->anyOf[0])]; + if (is_array($property->anyOf) && $property->anyOf[0] instanceof OpenAPiSchema/* && array_key_exists(spl_object_hash($property->anyOf[0]), $schemaClassNameMap)*/) { + $fqcnn = '\\' . $rootNamespace . '\\' . $schemaRegistry->get($property->anyOf[0]); $propertyStmt->setType($nullable . $fqcnn); $method->setReturnType($nullable . $fqcnn); $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\Hydrate(' . $fqcnn . '::class)'; $setDefaylt = false; - } else if (is_array($property->allOf) && $property->allOf[0] instanceof OpenAPiSchema && array_key_exists(spl_object_hash($property->allOf[0]), $schemaClassNameMap)) { - $fqcnn = '\\' . $rootNamespace . '\\' . $schemaClassNameMap[spl_object_hash($property->allOf[0])]; + } else if (is_array($property->allOf) && $property->allOf[0] instanceof OpenAPiSchema/* && array_key_exists(spl_object_hash($property->allOf[0]), $schemaClassNameMap)*/) { + $fqcnn = '\\' . $rootNamespace . '\\' . $schemaRegistry->get($property->allOf[0]); $propertyStmt->setType($nullable . $fqcnn); $method->setReturnType($nullable . $fqcnn); $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\Hydrate(' . $fqcnn . '::class)'; $setDefaylt = false; } - if ($property->type === 'object' && $property instanceof OpenAPiSchema && array_key_exists(spl_object_hash($property), $schemaClassNameMap)) { - $fqcnn = '\\' . $rootNamespace . '\\' . $schemaClassNameMap[spl_object_hash($property)]; + if ($property->type === 'object' && $property instanceof OpenAPiSchema/* && array_key_exists(spl_object_hash($property), $schemaClassNameMap)*/) { + $fqcnn = '\\' . $rootNamespace . '\\' . $schemaRegistry->get($property); $propertyStmt->setType($nullable . $fqcnn); $method->setReturnType($nullable . $fqcnn); $propertyDocBlock[] = '@\WyriHaximus\Hydrator\Attribute\Hydrate(' . $fqcnn . '::class)'; diff --git a/src/Generator/WebHook.php b/src/Generator/WebHook.php index e05927b..c0fc403 100644 --- a/src/Generator/WebHook.php +++ b/src/Generator/WebHook.php @@ -3,6 +3,7 @@ namespace ApiClients\Tools\OpenApiClientGenerator\Generator; use ApiClients\Tools\OpenApiClientGenerator\File; +use ApiClients\Tools\OpenApiClientGenerator\SchemaRegistry; use cebe\openapi\spec\Operation as OpenAPiOperation; use cebe\openapi\spec\PathItem; use Jawira\CaseConverter\Convert; @@ -26,7 +27,7 @@ final class WebHook * @return iterable * @throws \Jawira\CaseConverter\CaseConverterException */ - public static function generate(string $path, string $namespace, string $baseNamespace, string $className, PathItem $pathItem, array $schemaClassNameMap, string $rootNamespace): iterable + public static function generate(string $path, string $namespace, string $baseNamespace, string $className, PathItem $pathItem, SchemaRegistry $schemaRegistry, string $rootNamespace): iterable { $factory = new BuilderFactory(); $stmt = $factory->namespace($namespace); @@ -58,24 +59,30 @@ public static function generate(string $path, string $namespace, string $baseNam $tmts[] = new Node\Stmt\Label($gotoLabels); $gotoLabels++; //'{"title":"release created event","required":["action","release","repository","sender"],"type":"object","properties":{"action":{"enum":["created"],"type":"string"},"release":{"required":["url","assets_url","upload_url","html_url","id","node_id","tag_name","target_commitish","name","draft","author","prerelease","created_at","published_at","assets","tarball_url","zipball_url","body"],"type":"object","properties":{"url":{"type":"string","format":"uri"},"assets_url":{"type":"string","format":"uri"},"upload_url":{"type":"string","format":"uri-template"},"html_url":{"type":"string","format":"uri"},"id":{"type":"integer"},"node_id":{"type":"string"},"tag_name":{"type":"string","description":"The name of the tag."},"target_commitish":{"type":"string","description":"Specifies the commitish value that determines where the Git tag is created from."},"name":{"type":"null"},"draft":{"type":"boolean","description":"true to create a draft (unpublished) release, false to create a published one."},"author":{"title":"User","required":["login","id","node_id","avatar_url","gravatar_id","url","html_url","followers_url","following_url","gists_url","starred_url","subscriptions_url","organizations_url","repos_url","events_url","received_events_url","type","site_admin"],"type":"object","properties":{"login":{"type":"string"},"id":{"type":"integer"},"node_id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"avatar_url":{"type":"string","format":"uri"},"gravatar_id":{"type":"string"},"url":{"type":"string","format":"uri"},"html_url":{"type":"string","format":"uri"},"followers_url":{"type":"string","format":"uri"},"following_url":{"type":"string","format":"uri-template"},"gists_url":{"type":"string","format":"uri-template"},"starred_url":{"type":"string","format":"uri-template"},"subscriptions_url":{"type":"string","format":"uri"},"organizations_url":{"type":"string","format":"uri"},"repos_url":{"type":"string","format":"uri"},"events_url":{"type":"string","format":"uri-template"},"received_events_url":{"type":"string","format":"uri"},"type":{"enum":["Bot","User","Organization"],"type":"string"},"site_admin":{"type":"boolean"}},"additionalProperties":false},"prerelease":{"type":"boolean","description":"Whether the release is identified as a prerelease or a full release."},"created_at":{"type":["string","null"],"format":"date-time"},"published_at":{"type":["string","null"],"format":"date-time"},"assets":{"type":"array","items":{"title":"Release Asset","required":["url","browser_download_url","id","node_id","name","label","state","content_type","size","download_count","created_at","updated_at"],"type":"object","properties":{"url":{"type":"string","format":"uri"},"browser_download_url":{"type":"string","format":"uri"},"id":{"type":"integer"},"node_id":{"type":"string"},"name":{"type":"string","description":"The file name of the asset."},"label":{"type":"string"},"state":{"enum":["uploaded"],"type":"string","description":"State of the release asset."},"content_type":{"type":"string"},"size":{"type":"integer"},"download_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"uploader":{"title":"User","required":["login","id","node_id","avatar_url","gravatar_id","url","html_url","followers_url","following_url","gists_url","starred_url","subscriptions_url","organizations_url","repos_url","events_url","received_events_url","type","site_admin"],"type":"object","properties":{"login":{"type":"string"},"id":{"type":"integer"},"node_id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"avatar_url":{"type":"string","format":"uri"},"gravatar_id":{"type":"string"},"url":{"type":"string","format":"uri"},"html_url":{"type":"string","format":"uri"},"followers_url":{"type":"string","format":"uri"},"following_url":{"type":"string","format":"uri-template"},"gists_url":{"type":"string","format":"uri-template"},"starred_url":{"type":"string","format":"uri-template"},"subscriptions_url":{"type":"string","format":"uri"},"organizations_url":{"type":"string","format":"uri"},"repos_url":{"type":"string","format":"uri"},"events_url":{"type":"string","format":"uri-template"},"received_events_url":{"type":"string","format":"uri"},"type":{"enum":["Bot","User","Organization"],"type":"string"},"site_admin":{"type":"boolean"}},"additionalProperties":false}},"description":"Data related to a release.","additionalProperties":false}},"tarball_url":{"type":["string","null"],"format":"uri"},"zipball_url":{"type":["string","null"],"format":"uri"},"body":{"type":["string","null"]}},"description":"The [release](https:\\/\\/docs.github.com\\/en\\/rest\\/reference\\/repos\\/#get-a-release) object.","additionalProperties":false},"repository":{"title":"Repository","required":["id","node_id","name","full_name","private","owner","html_url","description","fork","url","forks_url","keys_url","collaborators_url","teams_url","hooks_url","issue_events_url","events_url","assignees_url","branches_url","tags_url","blobs_url","git_tags_url","git_refs_url","trees_url","statuses_url","languages_url","stargazers_url","contributors_url","subscribers_url","subscription_url","commits_url","git_commits_url","comments_url","issue_comment_url","contents_url","compare_url","merges_url","archive_url","downloads_url","issues_url","pulls_url","milestones_url","notifications_url","labels_url","releases_url","deployments_url","created_at","updated_at","pushed_at","git_url","ssh_url","clone_url","svn_url","homepage","size","stargazers_count","watchers_count","language","has_issues","has_projects","has_downloads","has_wiki","has_pages","forks_count","mirror_url","archived","open_issues_count","license","forks","open_issues","watchers","default_branch"],"type":"object","properties":{"id":{"type":"integer","description":"Unique identifier of the repository"},"node_id":{"type":"string"},"name":{"type":"string","description":"The name of the repository."},"full_name":{"type":"string"},"private":{"type":"boolean","description":"Whether the repository is private or public."},"owner":{"title":"User","required":["login","id","node_id","avatar_url","gravatar_id","url","html_url","followers_url","following_url","gists_url","starred_url","subscriptions_url","organizations_url","repos_url","events_url","received_events_url","type","site_admin"],"type":"object","properties":{"login":{"type":"string"},"id":{"type":"integer"},"node_id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"avatar_url":{"type":"string","format":"uri"},"gravatar_id":{"type":"string"},"url":{"type":"string","format":"uri"},"html_url":{"type":"string","format":"uri"},"followers_url":{"type":"string","format":"uri"},"following_url":{"type":"string","format":"uri-template"},"gists_url":{"type":"string","format":"uri-template"},"starred_url":{"type":"string","format":"uri-template"},"subscriptions_url":{"type":"string","format":"uri"},"organizations_url":{"type":"string","format":"uri"},"repos_url":{"type":"string","format":"uri"},"events_url":{"type":"string","format":"uri-template"},"received_events_url":{"type":"string","format":"uri"},"type":{"enum":["Bot","User","Organization"],"type":"string"},"site_admin":{"type":"boolean"}},"additionalProperties":false},"html_url":{"type":"string","format":"uri"},"description":{"type":["string","null"]},"fork":{"type":"boolean"},"url":{"type":"string","format":"uri"},"forks_url":{"type":"string","format":"uri"},"keys_url":{"type":"string","format":"uri-template"},"collaborators_url":{"type":"string","format":"uri-template"},"teams_url":{"type":"string","format":"uri"},"hooks_url":{"type":"string","format":"uri"},"issue_events_url":{"type":"string","format":"uri-template"},"events_url":{"type":"string","format":"uri"},"assignees_url":{"type":"string","format":"uri-template"},"branches_url":{"type":"string","format":"uri-template"},"tags_url":{"type":"string","format":"uri"},"blobs_url":{"type":"string","format":"uri-template"},"git_tags_url":{"type":"string","format":"uri-template"},"git_refs_url":{"type":"string","format":"uri-template"},"trees_url":{"type":"string","format":"uri-template"},"statuses_url":{"type":"string","format":"uri-template"},"languages_url":{"type":"string","format":"uri"},"stargazers_url":{"type":"string","format":"uri"},"contributors_url":{"type":"string","format":"uri"},"subscribers_url":{"type":"string","format":"uri"},"subscription_url":{"type":"string","format":"uri"},"commits_url":{"type":"string","format":"uri-template"},"git_commits_url":{"type":"string","format":"uri-template"},"comments_url":{"type":"string","format":"uri-template"},"issue_comment_url":{"type":"string","format":"uri-template"},"contents_url":{"type":"string","format":"uri-template"},"compare_url":{"type":"string","format":"uri-template"},"merges_url":{"type":"string","format":"uri"},"archive_url":{"type":"string","format":"uri-template"},"downloads_url":{"type":"string","format":"uri"},"issues_url":{"type":"string","format":"uri-template"},"pulls_url":{"type":"string","format":"uri-template"},"milestones_url":{"type":"string","format":"uri-template"},"notifications_url":{"type":"string","format":"uri-template"},"labels_url":{"type":"string","format":"uri-template"},"releases_url":{"type":"string","format":"uri-template"},"deployments_url":{"type":"string","format":"uri"},"created_at":{"oneOf":[{"type":"integer"},{"type":"string","format":"date-time"}]},"updated_at":{"type":"string","format":"date-time"},"pushed_at":{"oneOf":[{"type":"integer"},{"type":"string","format":"date-time"},{"type":"null"}]},"git_url":{"type":"string","format":"uri"},"ssh_url":{"type":"string"},"clone_url":{"type":"string","format":"uri"},"svn_url":{"type":"string","format":"uri"},"homepage":{"type":["string","null"]},"size":{"type":"integer"},"stargazers_count":{"type":"integer"},"watchers_count":{"type":"integer"},"language":{"type":["string","null"]},"has_issues":{"type":"boolean","description":"Whether issues are enabled.","default":true},"has_projects":{"type":"boolean","description":"Whether projects are enabled.","default":true},"has_downloads":{"type":"boolean","description":"Whether downloads are enabled.","default":true},"has_wiki":{"type":"boolean","description":"Whether the wiki is enabled.","default":true},"has_pages":{"type":"boolean"},"forks_count":{"type":"integer"},"mirror_url":{"type":["string","null"],"format":"uri"},"archived":{"type":"boolean","description":"Whether the repository is archived.","default":false},"disabled":{"type":"boolean","description":"Returns whether or not this repository is disabled."},"open_issues_count":{"type":"integer"},"license":{"oneOf":[{"title":"License","required":["key","name","spdx_id","url","node_id"],"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"spdx_id":{"type":"string"},"url":{"type":["string","null"],"format":"uri"},"node_id":{"type":"string"}},"additionalProperties":false},{"type":"null"}]},"forks":{"type":"integer"},"open_issues":{"type":"integer"},"watchers":{"type":"integer"},"stargazers":{"type":"integer"},"default_branch":{"type":"string","description":"The default branch of the repository."},"allow_squash_merge":{"type":"boolean","description":"Whether to allow squash merges for pull requests.","default":true},"allow_merge_commit":{"type":"boolean","description":"Whether to allow merge commits for pull requests.","default":true},"allow_rebase_merge":{"type":"boolean","description":"Whether to allow rebase merges for pull requests.","default":true},"delete_branch_on_merge":{"type":"boolean","description":"Whether to delete head branches when pull requests are merged","default":false},"master_branch":{"type":"string"},"permissions":{"required":["pull","push","admin"],"type":"object","properties":{"pull":{"type":"boolean"},"push":{"type":"boolean"},"admin":{"type":"boolean"},"maintain":{"type":"boolean"},"triage":{"type":"boolean"}},"additionalProperties":false},"public":{"type":"boolean"},"organization":{"type":"string"}},"description":"A git repository","additionalProperties":false},"sender":{"title":"User","required":["login","id","node_id","avatar_url","gravatar_id","url","html_url","followers_url","following_url","gists_url","starred_url","subscriptions_url","organizations_url","repos_url","events_url","received_events_url","type","site_admin"],"type":"object","properties":{"login":{"type":"string"},"id":{"type":"integer"},"node_id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"avatar_url":{"type":"string","format":"uri"},"gravatar_id":{"type":"string"},"url":{"type":"string","format":"uri"},"html_url":{"type":"string","format":"uri"},"followers_url":{"type":"string","format":"uri"},"following_url":{"type":"string","format":"uri-template"},"gists_url":{"type":"string","format":"uri-template"},"starred_url":{"type":"string","format":"uri-template"},"subscriptions_url":{"type":"string","format":"uri"},"organizations_url":{"type":"string","format":"uri"},"repos_url":{"type":"string","format":"uri"},"events_url":{"type":"string","format":"uri-template"},"received_events_url":{"type":"string","format":"uri"},"type":{"enum":["Bot","User","Organization"],"type":"string"},"site_admin":{"type":"boolean"}},"additionalProperties":false},"installation":{"title":"InstallationLite","required":["id","node_id"],"type":"object","properties":{"id":{"type":"integer","description":"The ID of the installation."},"node_id":{"type":"string"}},"description":"Installation","additionalProperties":false},"organization":{"title":"Organization","required":["login","id","node_id","url","repos_url","events_url","hooks_url","issues_url","members_url","public_members_url","avatar_url","description"],"type":"object","properties":{"login":{"type":"string"},"id":{"type":"integer"},"node_id":{"type":"string"},"url":{"type":"string","format":"uri"},"html_url":{"type":"string","format":"uri"},"repos_url":{"type":"string","format":"uri"},"events_url":{"type":"string","format":"uri"},"hooks_url":{"type":"string","format":"uri"},"issues_url":{"type":"string","format":"uri"},"members_url":{"type":"string","format":"uri-template"},"public_members_url":{"type":"string","format":"uri-template"},"avatar_url":{"type":"string","format":"uri"},"description":{"type":["string","null"]}},"additionalProperties":false}},"additionalProperties":false}' - $fabicatedSchema = [ + $fabicatedSchema = new \cebe\openapi\spec\Schema([ 'title' => $oneOfSchema->title, 'required' => [$content->schema->discriminator->propertyName], 'properties' => [ $name => $property->getSerializableData(), ], 'additionalProperties' => true, - ]; + ]); $tmts[] = new Node\Stmt\TryCatch([ new Node\Stmt\Expression(new Node\Expr\MethodCall( new Node\Expr\Variable('schemaValidator'), new Node\Name('validate'), [ new Node\Arg(new Node\Expr\Variable('data')), - new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [new Node\Scalar\String_(json_encode($fabicatedSchema)), new Node\Scalar\String_('\cebe\openapi\spec\Schema')])), + new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [ + new Node\Expr\ClassConstFetch( + new Node\Name('\\' . $rootNamespace . 'Schema\\' . $schemaRegistry->get($fabicatedSchema)), + new Node\Name('SCHEMA_JSON'), + ), + new Node\Scalar\String_('\cebe\openapi\spec\Schema'), + ])), ] )), - new Node\Stmt\Return_(new Node\Scalar\String_($rootNamespace . 'Schema\\' . $schemaClassNameMap[spl_object_hash($oneOfSchema)])), + new Node\Stmt\Return_(new Node\Scalar\String_($rootNamespace . 'Schema\\' . $schemaRegistry->get($oneOfSchema))), ], [ new Node\Stmt\Catch_( [new Node\Name('\\' . \Throwable::class)], @@ -93,9 +100,9 @@ public static function generate(string $path, string $namespace, string $baseNam $tmts[] = new Node\Stmt\Throw_(new Node\Expr\Variable($gotoLabels)); } else { foreach ($content->schema->oneOf as $oneOfSchema) { - if (!array_key_exists(spl_object_hash($oneOfSchema), $schemaClassNameMap)) { - continue; // TODO: Remove to make sure we have all schemas mapped - } +// if (!array_key_exists(spl_object_hash($oneOfSchema), $schemaClassNameMap)) { +// continue; // TODO: Remove to make sure we have all schemas mapped +// } $tmts[] = new Node\Stmt\Label($gotoLabels); $gotoLabels++; $tmts[] = new Node\Stmt\TryCatch([ @@ -104,10 +111,16 @@ public static function generate(string $path, string $namespace, string $baseNam new Node\Name('validate'), [ new Node\Arg(new Node\Expr\Variable('data')), - new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [new Node\Scalar\String_(json_encode($oneOfSchema->getSerializableData())), new Node\Scalar\String_('\cebe\openapi\spec\Schema')])), + new Node\Arg(new Node\Expr\StaticCall(new Node\Name('\cebe\openapi\Reader'), new Node\Name('readFromJson'), [ + new Node\Expr\ClassConstFetch( + new Node\Name('\\' . $rootNamespace . 'Schema\\' . $schemaRegistry->get($oneOfSchema)), + new Node\Name('SCHEMA_JSON'), + ), + new Node\Scalar\String_('\cebe\openapi\spec\Schema'), + ])), ] )), - new Node\Stmt\Return_(new Node\Scalar\String_($rootNamespace . 'Schema\\' . $schemaClassNameMap[spl_object_hash($oneOfSchema)])), + new Node\Stmt\Return_(new Node\Scalar\String_($rootNamespace . 'Schema\\' . $schemaRegistry->get($oneOfSchema))), ], [ new Node\Stmt\Catch_( [new Node\Name('\\' . \Throwable::class)], @@ -124,7 +137,7 @@ public static function generate(string $path, string $namespace, string $baseNam } if (count($tmts) === 0) { - $tmts[] = new Node\Stmt\Return_(new Node\Scalar\String_($rootNamespace . 'Schema\\' . $schemaClassNameMap[spl_object_hash($content->schema)])); + $tmts[] = new Node\Stmt\Return_(new Node\Scalar\String_($rootNamespace . 'Schema\\' . $schemaRegistry->get($content->schema))); } $method->addStmts($tmts); diff --git a/src/SchemaRegistry.php b/src/SchemaRegistry.php new file mode 100644 index 0000000..90f6c7a --- /dev/null +++ b/src/SchemaRegistry.php @@ -0,0 +1,55 @@ + + */ + private array $splHash = []; + /** + * @var array + */ + private array $json = []; + + /** + * @var array> + */ + private array $unknownSchemas = []; + + public function addClassName(string $className, Schema $schema): void + { + $this->splHash[spl_object_hash($schema)] = $className; + $this->json[json_encode($schema->getSerializableData())] = $className; + } + + public function get(\cebe\openapi\spec\Schema $schema): string + { + $hash = spl_object_hash($schema); + if (array_key_exists($hash, $this->splHash)) { + return $this->splHash[$hash]; + } + + $json = json_encode($schema->getSerializableData()); + if (array_key_exists($json, $this->json)) { + return $this->json[$json]; + } + + $name = 'c_' . md5($json); + $this->unknownSchemas[$hash] = [ + 'name' => $name, + 'className' => Generator::className('Unknown\C_' . md5($json)), + 'schema' => $schema, + ]; + + return $this->unknownSchemas[$hash]['className']; + } + + public function unknownSchemas(): iterable + { + yield from $this->unknownSchemas; + } +}