From 31daf14758a479ecb9442ca99531408d395f464f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Wed, 18 Sep 2024 16:59:54 +0200 Subject: [PATCH 1/9] operation openapi attribute support --- .../ApiPlatformCoreAttributeGenerator.php | 84 ++++++++++- tests/Command/GenerateCommandTest.php | 140 ++++++++++++++++++ tests/config/openapi-operation-property.yaml | 106 +++++++++++++ 3 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 tests/config/openapi-operation-property.yaml diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index f9bf1f0..d7ffb78 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -23,6 +23,9 @@ use ApiPlatform\Metadata\Patch; use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\OpenApi\Model\Parameter; +use ApiPlatform\OpenApi\Model\Response; use ApiPlatform\SchemaGenerator\Model\Attribute; use ApiPlatform\SchemaGenerator\Model\Class_; use ApiPlatform\SchemaGenerator\Model\Property; @@ -39,6 +42,21 @@ */ final class ApiPlatformCoreAttributeGenerator extends AbstractAttributeGenerator { + /** + * Hints for not typed array parameters. + */ + private const PRAMETER_TYPE_HINTS = [ + Operation::class => [ + 'responses' => Response::class . '[]', + 'parameters' => Parameter::class . '[]', + ], + ]; + + /** + * @var array> + */ + private static array $parameterTypes = []; + public function generateClassAttributes(Class_ $class): array { if ($class->hasChild || $class->isEnum()) { @@ -85,7 +103,24 @@ public function generateClassAttributes(Class_ $class): array unset($methodConfig['class']); } - $arguments['operations'][] = new Literal(\sprintf('new %s(...?:)', + if (\is_array($methodConfig['openapi'] ?? null)) { + $methodConfig['openapi'] = Literal::new( + 'Operation', + self::extractParameters(Operation::class, $methodConfig['openapi']) + ); + $class->addUse(new Use_(Operation::class)); + array_walk_recursive( + self::$parameterTypes, + function (?string $type) use ($class) { + if (null !== $type) { + $class->addUse(new Use_(str_replace('[]', '', $type))); + } + } + ); + } + + $arguments['operations'][] = new Literal(sprintf( + 'new %s(...?:)', $operationMetadataClass, ), [$methodConfig ?? []]); } @@ -95,6 +130,53 @@ public function generateClassAttributes(Class_ $class): array return [new Attribute('ApiResource', $arguments)]; } + /** + * @param class-string $type + * @param mixed[] $values + * + * @return mixed[] + */ + private static function extractParameters(string $type, array $values): array + { + $types = self::$parameterTypes[$type] ??= + (static::PRAMETER_TYPE_HINTS[$type] ?? []) + array_reduce( + (new \ReflectionClass($type))->getConstructor()?->getParameters() ?? [], + static fn(array $types, \ReflectionParameter $refl) => $types + [ + $refl->getName() => $refl->getType() instanceof \ReflectionNamedType + && !$refl->getType()->isBuiltin() + ? $refl->getType()->getName() + : null, + ], + [] + ); + + $parameters = array_intersect_key($values, $types); + foreach ($parameters as $name => $parameter) { + $type = $types[$name]; + if (null !== $type && \is_array($parameter)) { + $isArrayType = str_ends_with($type, '[]'); + $type = $isArrayType ? substr($type, 0, -2) : $type; + $shortName = (new \ReflectionClass($type))->getShortName(); + $parameters[$name] = $isArrayType + ? array_map( + static fn(array $values) => Literal::new( + $shortName, + self::extractParameters($type, $values) + ), + $parameter + ) + : Literal::new( + $shortName, + \ArrayObject::class === $type + ? [$parameter] + : self::extractParameters($type, $parameter) + ); + } + } + + return $parameters; + } + /** * Verifies that the operations' config is valid. * diff --git a/tests/Command/GenerateCommandTest.php b/tests/Command/GenerateCommandTest.php index f966dbb..f3868a6 100644 --- a/tests/Command/GenerateCommandTest.php +++ b/tests/Command/GenerateCommandTest.php @@ -561,4 +561,144 @@ public function testGenerationWithoutConfigFileQuestion(): void $this->assertEquals(0, $commandTester->execute(['output' => sys_get_temp_dir()])); $this->assertMatchesRegularExpression('/The entire vocabulary will be imported/', $commandTester->getDisplay()); } + + public function testOpenapiOperationProperty(): void + { + $outputDir = __DIR__.'/../../build/openapi-operation-property'; + $config = __DIR__.'/../config/openapi-operation-property.yaml'; + + $this->fs->mkdir($outputDir); + + $commandTester = new CommandTester(new GenerateCommand()); + $this->assertEquals(0, $commandTester->execute(['output' => $outputDir, 'config' => $config])); + $source = file_get_contents("$outputDir/App/Entity/Saml.php"); + + $this->assertStringContainsString(<<<'PHP' +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\OpenApi\Model\Parameter; +use ApiPlatform\OpenApi\Model\RequestBody; +use ApiPlatform\OpenApi\Model\Response; +PHP + , $source); + + $this->assertStringContainsString(<<<'PHP' +#[ApiResource( + shortName: 'Saml', + types: ['https://schema.org/Thing'], + operations: [ + new Get( + name: 'login', + uriTemplate: '/saml/{id}/login', + controller: 'App\Controller\SamlController::login', + openapi: new Operation( + tags: ['Auth'], + summary: 'SAML authentication.', + description: 'SAML authentication.', + responses: [ + 302 => new Response( + description: 'Initialization successful.', + headers: new \ArrayObject([ + 'Location' => [ + 'required' => true, + 'description' => 'SAML login page redirection.', + 'schema' => ['type' => 'string', 'format' => 'url'], + ], + ]), + ), + 403 => new Response(description: 'SAML disabled.'), + 404 => new Response(description: 'SAML not found.'), + ], + ), + ), + new Post( + name: 'acs', + uriTemplate: '/saml/{id}/acs', + controller: 'App\Controller\SamlController::acs', + inputFormats: ['urlencoded' => ['application/x-www-form-urlencoded']], + openapi: new Operation( + tags: ['Auth'], + summary: 'SAML ACS.', + description: 'SAML ACS.', + responses: [ + 302 => new Response( + description: 'Authentication successful.', + headers: new \ArrayObject([ + 'Location' => [ + 'required' => true, + 'description' => 'Redirection page.', + 'schema' => ['type' => 'string', 'format' => 'url'], + ], + ]), + ), + 401 => new Response(description: 'Authentication failed.'), + 403 => new Response(description: 'SAML disabled.'), + 404 => new Response(description: 'SAML not found.'), + ], + requestBody: new RequestBody( + required: true, + content: new \ArrayObject([ + 'application/x-www-form-urlencoded' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => ['SAMLResponse' => ['type' => 'string', 'description' => 'SAML login response.']], + ], + 'required' => ['SAMLResponse'], + ], + ]), + ), + ), + ), + new Get( + name: 'logout', + uriTemplate: '/saml/{id}/logout', + controller: 'App\Controller\SamlController::logout', + openapi: new Operation( + tags: ['Auth'], + summary: 'SAML logout.', + description: 'SAML logout.', + parameters: [ + new Parameter( + name: 'SAMLRequest', + in: 'query', + schema: ['type' => 'string'], + required: true, + description: 'SAML logout request.', + ), + new Parameter( + name: 'RelayState', + in: 'query', + schema: ['type' => 'string'], + required: false, + description: 'SAML logout response redirect URL.', + ), + new Parameter( + name: 'Signature', + in: 'query', + schema: ['type' => 'string'], + required: false, + description: 'SAML signature.', + ), + ], + responses: [ + 302 => new Response( + description: 'Logout successful.', + headers: new \ArrayObject([ + 'Location' => [ + 'required' => true, + 'description' => 'SAML logout response redirect URL.', + 'schema' => ['type' => 'string', 'format' => 'url'], + ], + ]), + ), + 403 => new Response(description: 'Logout failed.'), + 404 => new Response(description: 'SAML not found.'), + ], + ), + ), + ], +)] +PHP + , $source); + } } + diff --git a/tests/config/openapi-operation-property.yaml b/tests/config/openapi-operation-property.yaml new file mode 100644 index 0000000..d31c5ca --- /dev/null +++ b/tests/config/openapi-operation-property.yaml @@ -0,0 +1,106 @@ +types: + Saml: + operations: + login: + class: Get + name: "login" + uriTemplate: "/saml/{id}/login" + controller: "App\\Controller\\SamlController::login" + openapi: + tags: ["Auth"] + summary: "SAML authentication." + description: "SAML authentication." + responses: + 302: + description: "Initialization successful." + headers: + Location: + required: true + description: "SAML login page redirection." + schema: + type: "string" + format: "url" + 403: { description: "SAML disabled." } + 404: { description: "SAML not found." } + acs: + class: Post + name: "acs" + uriTemplate: "/saml/{id}/acs" + controller: "App\\Controller\\SamlController::acs" + inputFormats: + urlencoded: ['application/x-www-form-urlencoded'] + openapi: + tags: ["Auth"] + summary: "SAML ACS." + description: "SAML ACS." + responses: + 302: + description: "Authentication successful." + headers: + Location: + required: true + description: "Redirection page." + schema: + type: "string" + format: "url" + 401: { description: "Authentication failed."} + 403: { description: "SAML disabled." } + 404: { description: "SAML not found." } + requestBody: + required: true + content: + "application/x-www-form-urlencoded": + schema: + type: object + properties: + SAMLResponse: + type: string + description: "SAML login response." + required: ["SAMLResponse"] + logout: + class: Get + name: "logout" + uriTemplate: "/saml/{id}/logout" + controller: "App\\Controller\\SamlController::logout" + openapi: + tags: ["Auth"] + summary: "SAML logout." + description: "SAML logout." + parameters: + - name: "SAMLRequest" + in: "query" + schema: { type: "string" } + required: true + description: "SAML logout request." + - name: "RelayState" + in: "query" + schema: { type: "string" } + required: false + description: "SAML logout response redirect URL." + - name: "Signature" + in: "query" + schema: { type: "string" } + required: false + description: "SAML signature." + responses: + 302: + description: "Logout successful." + headers: + Location: + required: true + description: "SAML logout response redirect URL." + schema: + type: "string" + format: "url" + + 403: { description: "Logout failed." } + 404: { description: "SAML not found." } + attributes: + ApiResource: + shortName: "Saml" + properties: + name: + nullable: false + attributes: + ApiProperty: + iris: [ "https://schema.org/name" ] From 4f561301784726fc11bd3f0a10674e703d916545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 10:18:08 +0100 Subject: [PATCH 2/9] fix --- src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index d7ffb78..fd0b5c9 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -118,9 +118,7 @@ function (?string $type) use ($class) { } ); } - - $arguments['operations'][] = new Literal(sprintf( - 'new %s(...?:)', + $arguments['operations'][] = new Literal(\sprintf('new %s(...?:)', $operationMetadataClass, ), [$methodConfig ?? []]); } From 57eb6c07f086453074e2326ba267d428f54aad84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 11:56:04 +0100 Subject: [PATCH 3/9] arrow func return typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kévin Dunglas --- src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index fd0b5c9..1106246 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -157,7 +157,7 @@ private static function extractParameters(string $type, array $values): array $shortName = (new \ReflectionClass($type))->getShortName(); $parameters[$name] = $isArrayType ? array_map( - static fn(array $values) => Literal::new( + static fn (array $values): Literal => Literal::new( $shortName, self::extractParameters($type, $values) ), From 373fc44054f94ca0bc9ce8bb7968661f11ad45c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 11:56:27 +0100 Subject: [PATCH 4/9] arrow func return typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kévin Dunglas --- src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index 1106246..198b726 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -139,7 +139,7 @@ private static function extractParameters(string $type, array $values): array $types = self::$parameterTypes[$type] ??= (static::PRAMETER_TYPE_HINTS[$type] ?? []) + array_reduce( (new \ReflectionClass($type))->getConstructor()?->getParameters() ?? [], - static fn(array $types, \ReflectionParameter $refl) => $types + [ + static fn (array $types, \ReflectionParameter $refl): array => $types + [ $refl->getName() => $refl->getType() instanceof \ReflectionNamedType && !$refl->getType()->isBuiltin() ? $refl->getType()->getName() From c37e52fe2762a0766d6a7f2da335ffbbb3d5783e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 11:56:41 +0100 Subject: [PATCH 5/9] arrow func return typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kévin Dunglas --- src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index 198b726..923864e 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -111,7 +111,7 @@ public function generateClassAttributes(Class_ $class): array $class->addUse(new Use_(Operation::class)); array_walk_recursive( self::$parameterTypes, - function (?string $type) use ($class) { + function (?string $type): void use ($class) { if (null !== $type) { $class->addUse(new Use_(str_replace('[]', '', $type))); } From 7fedb421bfbb0c88de1984cd1a3c3b712300f022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 11:57:13 +0100 Subject: [PATCH 6/9] styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kévin Dunglas --- src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index 923864e..b03a353 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -118,6 +118,7 @@ function (?string $type): void use ($class) { } ); } + $arguments['operations'][] = new Literal(\sprintf('new %s(...?:)', $operationMetadataClass, ), [$methodConfig ?? []]); From d1255d7c6b40d235c59015de06bb54ec3a80fd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 12:13:53 +0100 Subject: [PATCH 7/9] anon func return typing --- src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index b03a353..a93f325 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -111,7 +111,7 @@ public function generateClassAttributes(Class_ $class): array $class->addUse(new Use_(Operation::class)); array_walk_recursive( self::$parameterTypes, - function (?string $type): void use ($class) { + function (?string $type) use ($class): void { if (null !== $type) { $class->addUse(new Use_(str_replace('[]', '', $type))); } From 1828a8915408a519f6233d4804cf7128117735df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Suwi=C5=84ski?= Date: Thu, 27 Feb 2025 15:55:41 +0100 Subject: [PATCH 8/9] no declarative code refacto --- .../ApiPlatformCoreAttributeGenerator.php | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index a93f325..7339d4b 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -148,28 +148,55 @@ private static function extractParameters(string $type, array $values): array ], [] ); + if (isset(self::$parameterTypes[$type])) { + $types = self::$parameterTypes[$type]; + } else { + $types = static::PRAMETER_TYPE_HINTS[$type] ?? []; + $parameterRefls = (new \ReflectionClass($type)) + ->getConstructor() + ?->getParameters() ?? []; + foreach ($parameterRefls as $refl) { + $paramName = $refl->getName(); + if (\array_key_exists($paramName, $types)) { + continue; + } + $paramType = $refl->getType(); + if ($paramType instanceof \ReflectionNamedType && !$paramType->isBuiltin()) { + $types[$paramName] = $paramType->getName(); + } else { + $types[$paramName] = null; + } + } + self::$parameterTypes[$type] = $types; + } $parameters = array_intersect_key($values, $types); foreach ($parameters as $name => $parameter) { $type = $types[$name]; - if (null !== $type && \is_array($parameter)) { - $isArrayType = str_ends_with($type, '[]'); - $type = $isArrayType ? substr($type, 0, -2) : $type; - $shortName = (new \ReflectionClass($type))->getShortName(); - $parameters[$name] = $isArrayType - ? array_map( - static fn (array $values): Literal => Literal::new( - $shortName, - self::extractParameters($type, $values) - ), - $parameter - ) - : Literal::new( + if (null === $type || !\is_array($parameter)) { + continue; + } + $isArrayType = str_ends_with($type, '[]'); + /** + * @var class-string + */ + $type = $isArrayType ? substr($type, 0, -2) : $type; + $shortName = (new \ReflectionClass($type))->getShortName(); + if ($isArrayType) { + $parameters[$name] = []; + foreach ($parameter as $key => $values) { + $parameters[$name][$key] = Literal::new( $shortName, - \ArrayObject::class === $type - ? [$parameter] - : self::extractParameters($type, $parameter) + self::extractParameters($type, $values) ); + } + } else { + $parameters[$name] = Literal::new( + $shortName, + \ArrayObject::class === $type + ? [$parameter] + : self::extractParameters($type, $parameter) + ); } } From b69d784a4c6f1ebc421c85e261ad2723dc00f4d5 Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 28 Jan 2026 21:00:28 +0100 Subject: [PATCH 9/9] cs --- scoper.inc.php | 6 +++--- .../ApiPlatformCoreAttributeGenerator.php | 6 +++--- .../ConfigurationAttributeGenerator.php | 2 +- .../DoctrineMongoDBAttributeGenerator.php | 4 ++-- src/AttributeGenerator/DoctrineOrmAttributeGenerator.php | 4 ++-- src/ClassMutator/ClassPropertiesAppender.php | 2 +- src/Model/AddAttributeTrait.php | 4 ++-- src/Model/Type/UnionType.php | 4 ++-- src/SchemaGeneratorConfiguration.php | 8 ++++---- src/TypesGenerator.php | 2 +- tests/Command/GenerateCommandTest.php | 7 ++----- 11 files changed, 23 insertions(+), 26 deletions(-) diff --git a/scoper.inc.php b/scoper.inc.php index c404d2b..14df3fa 100644 --- a/scoper.inc.php +++ b/scoper.inc.php @@ -17,7 +17,7 @@ ApiPlatform\Core\Annotation\ApiResource::class, ], 'patchers' => [ - function (string $filePath, string $prefix, string $content): string { + static function (string $filePath, string $prefix, string $content): string { // // PHP-CS-Fixer patch // @@ -35,7 +35,7 @@ function (string $filePath, string $prefix, string $content): string { // TODO: Temporary patch until the issue is fixed upstream // @link https://github.com/humbug/php-scoper/issues/285 - function (string $filePath, string $prefix, string $content): string { + static function (string $filePath, string $prefix, string $content): string { if (!str_contains($content, '@')) { return $content; } @@ -51,7 +51,7 @@ function (string $filePath, string $prefix, string $content): string { $content ); }, - function (string $filePath, string $prefix, string $content): string { + static function (string $filePath, string $prefix, string $content): string { if (!str_starts_with($filePath, 'src/AnnotationGenerator/')) { return $content; } diff --git a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php index 7339d4b..b3df1a0 100644 --- a/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php +++ b/src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php @@ -47,8 +47,8 @@ final class ApiPlatformCoreAttributeGenerator extends AbstractAttributeGenerator */ private const PRAMETER_TYPE_HINTS = [ Operation::class => [ - 'responses' => Response::class . '[]', - 'parameters' => Parameter::class . '[]', + 'responses' => Response::class.'[]', + 'parameters' => Parameter::class.'[]', ], ]; @@ -111,7 +111,7 @@ public function generateClassAttributes(Class_ $class): array $class->addUse(new Use_(Operation::class)); array_walk_recursive( self::$parameterTypes, - function (?string $type) use ($class): void { + static function (?string $type) use ($class): void { if (null !== $type) { $class->addUse(new Use_(str_replace('[]', '', $type))); } diff --git a/src/AttributeGenerator/ConfigurationAttributeGenerator.php b/src/AttributeGenerator/ConfigurationAttributeGenerator.php index e06c2df..9431fbe 100644 --- a/src/AttributeGenerator/ConfigurationAttributeGenerator.php +++ b/src/AttributeGenerator/ConfigurationAttributeGenerator.php @@ -31,7 +31,7 @@ public function generateClassAttributes(Class_ $class): array $getAttributesNames = static fn (array $config) => $config === [[]] ? [] - : array_unique(array_map(fn (array $v) => array_keys($v)[0], $config)); + : array_unique(array_map(static fn (array $v) => array_keys($v)[0], $config)); $typeAttributesNames = $getAttributesNames($typeAttributes); $vocabAttributesNames = $getAttributesNames($vocabAttributes); diff --git a/src/AttributeGenerator/DoctrineMongoDBAttributeGenerator.php b/src/AttributeGenerator/DoctrineMongoDBAttributeGenerator.php index ed0ba33..8aecf29 100644 --- a/src/AttributeGenerator/DoctrineMongoDBAttributeGenerator.php +++ b/src/AttributeGenerator/DoctrineMongoDBAttributeGenerator.php @@ -54,14 +54,14 @@ public function generateClassAttributes(Class_ $class): array $directChildren = array_merge($directChildren, array_filter($this->classes, fn (Class_ $childClass) => $parentName === $childClass->parent())); } $parentNames = array_keys($directChildren); - $childNames = array_merge($childNames, array_keys(array_filter($directChildren, fn (Class_ $childClass) => !$childClass->isAbstract))); + $childNames = array_merge($childNames, array_keys(array_filter($directChildren, static fn (Class_ $childClass) => !$childClass->isAbstract))); } $mapNames = array_merge([$class->name()], $childNames); $attributes[] = new Attribute('MongoDB\Document'); $attributes[] = new Attribute('MongoDB\InheritanceType', [\in_array($this->config['doctrine']['inheritanceType'], ['SINGLE_COLLECTION', 'COLLECTION_PER_CLASS', 'NONE'], true) ? $this->config['doctrine']['inheritanceType'] : 'SINGLE_COLLECTION']); $attributes[] = new Attribute('MongoDB\DiscriminatorField', ['discr']); - $attributes[] = new Attribute('MongoDB\DiscriminatorMap', [array_reduce($mapNames, fn (array $map, string $mapName) => $map + [u($mapName)->camel()->toString() => new Literal(\sprintf('%s::class', $mapName))], [])]); + $attributes[] = new Attribute('MongoDB\DiscriminatorMap', [array_reduce($mapNames, static fn (array $map, string $mapName) => $map + [u($mapName)->camel()->toString() => new Literal(\sprintf('%s::class', $mapName))], [])]); } else { $attributes[] = new Attribute('MongoDB\Document'); } diff --git a/src/AttributeGenerator/DoctrineOrmAttributeGenerator.php b/src/AttributeGenerator/DoctrineOrmAttributeGenerator.php index 72938de..5e2c8e4 100644 --- a/src/AttributeGenerator/DoctrineOrmAttributeGenerator.php +++ b/src/AttributeGenerator/DoctrineOrmAttributeGenerator.php @@ -62,14 +62,14 @@ public function generateClassAttributes(Class_ $class): array $directChildren = array_merge($directChildren, array_filter($this->classes, fn (Class_ $childClass) => $parentName === $childClass->parent())); } $parentNames = array_keys($directChildren); - $childNames = array_merge($childNames, array_keys(array_filter($directChildren, fn (Class_ $childClass) => !$childClass->isAbstract))); + $childNames = array_merge($childNames, array_keys(array_filter($directChildren, static fn (Class_ $childClass) => !$childClass->isAbstract))); } $mapNames = array_merge([$class->name()], $childNames); $attributes[] = new Attribute('ORM\Entity'); $attributes[] = new Attribute('ORM\InheritanceType', [\in_array($this->config['doctrine']['inheritanceType'], ['JOINED', 'SINGLE_TABLE', 'TABLE_PER_CLASS', 'NONE'], true) ? $this->config['doctrine']['inheritanceType'] : 'JOINED']); $attributes[] = new Attribute('ORM\DiscriminatorColumn', ['name' => 'discr']); - $attributes[] = new Attribute('ORM\DiscriminatorMap', [array_reduce($mapNames, fn (array $map, string $mapName) => $map + [u($mapName)->camel()->toString() => new Literal(\sprintf('%s::class', $mapName))], [])]); + $attributes[] = new Attribute('ORM\DiscriminatorMap', [array_reduce($mapNames, static fn (array $map, string $mapName) => $map + [u($mapName)->camel()->toString() => new Literal(\sprintf('%s::class', $mapName))], [])]); } else { $attributes[] = new Attribute('ORM\Entity'); } diff --git a/src/ClassMutator/ClassPropertiesAppender.php b/src/ClassMutator/ClassPropertiesAppender.php index 78d04cd..d81d171 100644 --- a/src/ClassMutator/ClassPropertiesAppender.php +++ b/src/ClassMutator/ClassPropertiesAppender.php @@ -158,7 +158,7 @@ private function getParentClasses(array $graphs, RdfResource $resource, array $p return $this->getParentClasses($graphs, $resource, [$resource]); } - $filterBNodes = fn ($parentClasses) => array_filter($parentClasses, fn ($parentClass) => !$parentClass->isBNode()); + $filterBNodes = static fn ($parentClasses) => array_filter($parentClasses, static fn ($parentClass) => !$parentClass->isBNode()); if (!$subclasses = $resource->all('rdfs:subClassOf', 'resource')) { return $filterBNodes($parentClasses); } diff --git a/src/Model/AddAttributeTrait.php b/src/Model/AddAttributeTrait.php index a30d0d6..4052467 100644 --- a/src/Model/AddAttributeTrait.php +++ b/src/Model/AddAttributeTrait.php @@ -25,7 +25,7 @@ public function addAttribute(Attribute $attribute): self } } else { $this->attributes = array_map( - fn (Attribute $attr) => $attr->name() === $attribute->name() + static fn (Attribute $attr) => $attr->name() === $attribute->name() ? new Attribute($attr->name(), array_merge( $attr->args(), $attribute->args(), @@ -44,7 +44,7 @@ public function getAttributeWithName(string $name): ?Attribute { return array_values(array_filter( $this->attributes, - fn (Attribute $attr) => $attr->name() === $name + static fn (Attribute $attr) => $attr->name() === $name ))[0] ?? null; } } diff --git a/src/Model/Type/UnionType.php b/src/Model/Type/UnionType.php index 3fd09c8..5fd27a3 100644 --- a/src/Model/Type/UnionType.php +++ b/src/Model/Type/UnionType.php @@ -28,7 +28,7 @@ public function __construct(array $types) public function __toString(): string { - return implode('|', array_map(fn (Type $type) => $type instanceof CompositeType ? '('.$type.')' : $type, $this->types)); + return implode('|', array_map(static fn (Type $type) => $type instanceof CompositeType ? '('.$type.')' : $type, $this->types)); } public function getPhp(): string @@ -39,6 +39,6 @@ public function getPhp(): string $phpTypes[$type->getPhp()] = $type; } - return implode('|', array_map(fn (Type $type) => $type instanceof CompositeType ? '('.$type->getPhp().')' : $type->getPhp(), $phpTypes)); + return implode('|', array_map(static fn (Type $type) => $type instanceof CompositeType ? '('.$type->getPhp().')' : $type->getPhp(), $phpTypes)); } } diff --git a/src/SchemaGeneratorConfiguration.php b/src/SchemaGeneratorConfiguration.php index bb6db5a..8ca95e1 100644 --- a/src/SchemaGeneratorConfiguration.php +++ b/src/SchemaGeneratorConfiguration.php @@ -46,14 +46,14 @@ public function getConfigTreeBuilder(): TreeBuilder $namespacePrefix = $this->defaultPrefix ?? 'App\\'; /* @see https://yaml.org/type/omap.html */ - $transformOmap = fn (array $nodeConfig) => array_map( - fn ($v, $k) => \is_int($k) ? $v : [$k => $v], + $transformOmap = static fn (array $nodeConfig) => array_map( + static fn ($v, $k) => \is_int($k) ? $v : [$k => $v], array_values($nodeConfig), array_keys($nodeConfig) ); // @phpstan-ignore-next-line node is not null - $attributesNode = fn () => (new NodeBuilder()) + $attributesNode = static fn () => (new NodeBuilder()) ->arrayNode('attributes') ->info('Attributes (merged with generated attributes)') ->variablePrototype()->end() @@ -78,7 +78,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultValue([self::SCHEMA_ORG_URI => ['format' => 'rdfxml']]) ->beforeNormalization() ->ifArray() - ->then(fn (array $v) => array_map(fn ($rdf) => \is_scalar($rdf) ? ['uri' => $rdf] : $rdf, $v)) + ->then(static fn (array $v) => array_map(static fn ($rdf) => \is_scalar($rdf) ? ['uri' => $rdf] : $rdf, $v)) ->end() ->useAttributeAsKey('uri') ->arrayPrototype() diff --git a/src/TypesGenerator.php b/src/TypesGenerator.php index 23f3ba7..d42bf7f 100644 --- a/src/TypesGenerator.php +++ b/src/TypesGenerator.php @@ -313,7 +313,7 @@ private function getParentClasses(array $graphs, RdfResource $resource, array $p return $this->getParentClasses($graphs, $resource, [$resource]); } - $filterBNodes = fn ($parentClasses) => array_filter($parentClasses, fn ($parentClass) => !$parentClass->isBNode()); + $filterBNodes = static fn ($parentClasses) => array_filter($parentClasses, static fn ($parentClass) => !$parentClass->isBNode()); if (!$subclasses = $resource->all('rdfs:subClassOf', 'resource')) { return $filterBNodes($parentClasses); } diff --git a/tests/Command/GenerateCommandTest.php b/tests/Command/GenerateCommandTest.php index f3868a6..e8d2914 100644 --- a/tests/Command/GenerateCommandTest.php +++ b/tests/Command/GenerateCommandTest.php @@ -578,8 +578,7 @@ public function testOpenapiOperationProperty(): void use ApiPlatform\OpenApi\Model\Parameter; use ApiPlatform\OpenApi\Model\RequestBody; use ApiPlatform\OpenApi\Model\Response; -PHP - , $source); +PHP, $source); $this->assertStringContainsString(<<<'PHP' #[ApiResource( @@ -697,8 +696,6 @@ public function testOpenapiOperationProperty(): void ), ], )] -PHP - , $source); +PHP, $source); } } -