From 17fb3a9e001327840552840b47266520e44b26ac Mon Sep 17 00:00:00 2001 From: AI Date: Sun, 28 Dec 2025 15:36:42 +0000 Subject: [PATCH] Remove doctrine/annotations dependency, use PHP 8 native attributes only - Remove doctrine/annotations and koriym/attributes dependencies - Create AttributeLoader to replace DoctrineAnnotationLoader - Inline attribute reading directly into AttributeLoader (no separate reader class) - Remove LoaderFactory directory (DualReaderFactory, NativeAttributeReader) - Delete obsolete wrapper classes: Negotiations, RequestParameters, Responses - Remove @Annotation, @Target, @NamedArgumentConstructor docblock annotations - Replace AnnotationException with InvalidArgumentException - Update SymfonyValidator to use only attribute-based validation - Convert all test fixtures from docblock annotations to PHP 8 attributes - Rename test files to reflect new naming - Require nette/utils ^4.1.0 This is a breaking change - docblock annotations are no longer supported. --- composer.json | 8 +- phpstan.neon | 4 - src/Core/Annotation/Controller/Id.php | 11 +- src/Core/Annotation/Controller/Method.php | 13 +- .../Annotation/Controller/Negotiation.php | 9 +- .../Annotation/Controller/Negotiations.php | 45 ---- src/Core/Annotation/Controller/OpenApi.php | 9 +- src/Core/Annotation/Controller/Path.php | 11 +- .../Annotation/Controller/RequestBody.php | 9 +- .../Controller/RequestParameter.php | 15 +- .../Controller/RequestParameters.php | 67 ------ src/Core/Annotation/Controller/Response.php | 13 +- src/Core/Annotation/Controller/Responses.php | 59 ----- src/Core/Annotation/Controller/Tag.php | 11 +- ...notationLoader.php => AttributeLoader.php} | 205 ++++++++---------- .../DI/LoaderFactory/DualReaderFactory.php | 30 --- src/Core/DI/Plugin/CoreSchemaPlugin.php | 4 +- .../Mapping/Validator/SymfonyValidator.php | 13 -- .../Cases/Core/Annotation/Controller/Id.phpt | 3 +- .../Core/Annotation/Controller/Method.phpt | 5 +- .../Annotation/Controller/Negotiations.phpt | 27 --- .../Core/Annotation/Controller/Path.phpt | 3 +- .../Controller/RequestParameter.phpt | 5 +- .../Controller/RequestParameters.phpt | 51 ----- .../Cases/Core/Annotation/Controller/Tag.phpt | 3 +- tests/Cases/Core/DI/ApiExtension.phpt | 6 +- ...tationLoader.phpt => AttributeLoader.phpt} | 15 +- ...nLoader.phpt => AttributeMultiLoader.phpt} | 13 +- ...nLoader.phpt => MixedAttributeLoader.phpt} | 6 +- .../Mapping/Validator/SymfonyValidator.phpt | 7 +- .../Controllers/AbstractController.php | 6 +- .../AnnotationFoobarController.php | 51 ++--- .../Controllers/AnnotationMultiController.php | 33 +-- .../Fixtures/Controllers/ApiV1Controller.php | 4 +- .../Mixed/AnnotationAttributeController.php | 7 +- .../Mixed/PathAndRequestParamsController.php | 9 +- 36 files changed, 181 insertions(+), 609 deletions(-) delete mode 100644 src/Core/Annotation/Controller/Negotiations.php delete mode 100644 src/Core/Annotation/Controller/RequestParameters.php delete mode 100644 src/Core/Annotation/Controller/Responses.php rename src/Core/DI/Loader/{DoctrineAnnotationLoader.php => AttributeLoader.php} (53%) delete mode 100644 src/Core/DI/LoaderFactory/DualReaderFactory.php delete mode 100644 tests/Cases/Core/Annotation/Controller/Negotiations.phpt delete mode 100644 tests/Cases/Core/Annotation/Controller/RequestParameters.phpt rename tests/Cases/Core/DI/Loader/{DoctrineAnnotationLoader.phpt => AttributeLoader.phpt} (87%) rename tests/Cases/Core/DI/Loader/{DoctrineMultiAnnotationLoader.phpt => AttributeMultiLoader.phpt} (91%) rename tests/Cases/Core/DI/Loader/{MixedAttributeAnnotationLoader.phpt => MixedAttributeLoader.phpt} (90%) diff --git a/composer.json b/composer.json index 96bb3da6..be0f1771 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,13 @@ { "name": "contributte/apitte", - "description": "An opinionated and enjoyable API framework based on Nette Framework. Supporting content negotiation, debugging, middlewares, attributes, annotations and loving openapi/swagger.", + "description": "An opinionated and enjoyable API framework based on Nette Framework. Supporting content negotiation, debugging, middlewares, attributes and loving openapi/swagger.", "keywords": [ "api", "apitte", "http", "rest", "nette", - "annotation" + "attribute" ], "type": "library", "license": "MIT", @@ -25,9 +25,7 @@ "contributte/psr7-http-message": "^0.9.0 || ^0.10.0", "contributte/middlewares": "^0.11.0 || ^0.12.0", "contributte/openapi": "^0.1.0", - "doctrine/annotations": "^1.14.3 || ^2.0.0", - "koriym/attributes": "^1.0.5", - "nette/utils": "^4.0.0" + "nette/utils": "^4.1.0" }, "require-dev": { "contributte/qa": "^0.4", diff --git a/phpstan.neon b/phpstan.neon index ee8ef80c..e5c21e63 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -47,10 +47,6 @@ parameters: - message: "#^Parameter \\#2 \\$array of function implode expects array\\, array\\\\|string\\> given\\.$#" path: %currentWorkingDirectory%/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php - # Support for doctrine/annotations ^1 - - message: "#^Call to function method_exists\\(\\) with 'Doctrine\\\\\\\\Common\\\\\\\\Annotations\\\\\\\\AnnotationRegistry' and 'registerUniqueLoader' will always evaluate to false\\.$#" - path: src/Core/DI/LoaderFactory/DualReaderFactory.php - - message: "#^Dead catch - TypeError is never thrown in the try block.$#" path: src/Core/Mapping/Parameter/DateTimeTypeMapper.php count: 1 diff --git a/src/Core/Annotation/Controller/Id.php b/src/Core/Annotation/Controller/Id.php index 3e9dde80..1343cd87 100644 --- a/src/Core/Annotation/Controller/Id.php +++ b/src/Core/Annotation/Controller/Id.php @@ -3,15 +3,8 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; -use Doctrine\Common\Annotations\AnnotationException; +use InvalidArgumentException; -/** - * @Annotation - * @Target({"CLASS","METHOD"}) - * @NamedArgumentConstructor() - */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] class Id { @@ -21,7 +14,7 @@ public function __construct( ) { if ($name === '') { - throw new AnnotationException('Empty @Id given'); + throw new InvalidArgumentException('Empty #[Id] given'); } } diff --git a/src/Core/Annotation/Controller/Method.php b/src/Core/Annotation/Controller/Method.php index 5bb011b0..0647e460 100644 --- a/src/Core/Annotation/Controller/Method.php +++ b/src/Core/Annotation/Controller/Method.php @@ -3,15 +3,8 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; -use Doctrine\Common\Annotations\AnnotationException; - -/** - * @Annotation - * @Target("METHOD") - * @NamedArgumentConstructor() - */ +use InvalidArgumentException; + #[Attribute(Attribute::TARGET_METHOD)] class Method { @@ -25,7 +18,7 @@ class Method public function __construct(array|string $methods) { if (empty($methods)) { - throw new AnnotationException('Empty @Method given'); + throw new InvalidArgumentException('Empty #[Method] given'); } // Wrap single given method into array diff --git a/src/Core/Annotation/Controller/Negotiation.php b/src/Core/Annotation/Controller/Negotiation.php index ddf9b4be..71f7159d 100644 --- a/src/Core/Annotation/Controller/Negotiation.php +++ b/src/Core/Annotation/Controller/Negotiation.php @@ -3,14 +3,7 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; - -/** - * @Annotation - * @Target("ANNOTATION") - * @NamedArgumentConstructor() - */ + #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Negotiation { diff --git a/src/Core/Annotation/Controller/Negotiations.php b/src/Core/Annotation/Controller/Negotiations.php deleted file mode 100644 index c0fc450c..00000000 --- a/src/Core/Annotation/Controller/Negotiations.php +++ /dev/null @@ -1,45 +0,0 @@ -negotiations = $negotiations; - } - - /** - * @return Negotiation[] - */ - public function getNegotiations(): array - { - return $this->negotiations; - } - -} diff --git a/src/Core/Annotation/Controller/OpenApi.php b/src/Core/Annotation/Controller/OpenApi.php index 66b83549..02934b73 100644 --- a/src/Core/Annotation/Controller/OpenApi.php +++ b/src/Core/Annotation/Controller/OpenApi.php @@ -3,14 +3,7 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; - -/** - * @Annotation - * @Target({"CLASS","METHOD"}) - * @NamedArgumentConstructor() - */ + #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] class OpenApi { diff --git a/src/Core/Annotation/Controller/Path.php b/src/Core/Annotation/Controller/Path.php index 4367db46..75dd7ecf 100644 --- a/src/Core/Annotation/Controller/Path.php +++ b/src/Core/Annotation/Controller/Path.php @@ -3,15 +3,8 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; -use Doctrine\Common\Annotations\AnnotationException; +use InvalidArgumentException; -/** - * @Annotation - * @Target({"CLASS","METHOD"}) - * @NamedArgumentConstructor() - */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] class Path { @@ -21,7 +14,7 @@ public function __construct( ) { if ($path === '') { - throw new AnnotationException('Empty @Path given'); + throw new InvalidArgumentException('Empty #[Path] given'); } } diff --git a/src/Core/Annotation/Controller/RequestBody.php b/src/Core/Annotation/Controller/RequestBody.php index 5e4d381f..8853319d 100644 --- a/src/Core/Annotation/Controller/RequestBody.php +++ b/src/Core/Annotation/Controller/RequestBody.php @@ -3,14 +3,7 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; - -/** - * @Annotation - * @Target("METHOD") - * @NamedArgumentConstructor() - */ + #[Attribute(Attribute::TARGET_METHOD)] class RequestBody { diff --git a/src/Core/Annotation/Controller/RequestParameter.php b/src/Core/Annotation/Controller/RequestParameter.php index 1d76cac5..808c2d74 100644 --- a/src/Core/Annotation/Controller/RequestParameter.php +++ b/src/Core/Annotation/Controller/RequestParameter.php @@ -4,15 +4,8 @@ use Apitte\Core\Schema\EndpointParameter; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; -use Doctrine\Common\Annotations\AnnotationException; +use InvalidArgumentException; -/** - * @Annotation - * @Target("ANNOTATION") - * @NamedArgumentConstructor() - */ #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class RequestParameter { @@ -32,15 +25,15 @@ public function __construct( ) { if ($name === '') { - throw new AnnotationException('Empty @RequestParameter name given'); + throw new InvalidArgumentException('Empty #[RequestParameter] name given'); } if ($type === '') { - throw new AnnotationException('Empty @RequestParameter type given'); + throw new InvalidArgumentException('Empty #[RequestParameter] type given'); } if (!in_array($in, EndpointParameter::IN, true)) { - throw new AnnotationException('Invalid @RequestParameter in given'); + throw new InvalidArgumentException('Invalid #[RequestParameter] in given'); } } diff --git a/src/Core/Annotation/Controller/RequestParameters.php b/src/Core/Annotation/Controller/RequestParameters.php deleted file mode 100644 index e319c9bd..00000000 --- a/src/Core/Annotation/Controller/RequestParameters.php +++ /dev/null @@ -1,67 +0,0 @@ -validateUniqueNames($parameters); - - $this->parameters = $parameters; - } - - /** - * @return RequestParameter[] - */ - public function getParameters(): array - { - return $this->parameters; - } - - /** - * @param RequestParameter[] $parameters - */ - private function validateUniqueNames(array $parameters): void - { - $takenNames = []; - - foreach ($parameters as $parameter) { - if (!isset($takenNames[$parameter->getIn()][$parameter->getName()])) { - $takenNames[$parameter->getIn()][$parameter->getName()] = $parameter; - } else { - throw new AnnotationException(sprintf( - 'Multiple @RequestParameter annotations with "name=%s" and "in=%s" given. Each parameter must have unique combination of location and name.', - $parameter->getName(), - $parameter->getIn() - )); - } - } - } - -} diff --git a/src/Core/Annotation/Controller/Response.php b/src/Core/Annotation/Controller/Response.php index 433574ff..51e8b52c 100644 --- a/src/Core/Annotation/Controller/Response.php +++ b/src/Core/Annotation/Controller/Response.php @@ -3,15 +3,8 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; -use Doctrine\Common\Annotations\AnnotationException; - -/** - * @Annotation - * @Target("ANNOTATION") - * @NamedArgumentConstructor() - */ +use InvalidArgumentException; + #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Response { @@ -23,7 +16,7 @@ public function __construct( ) { if (empty($description)) { - throw new AnnotationException('Empty @Response description given'); + throw new InvalidArgumentException('Empty #[Response] description given'); } } diff --git a/src/Core/Annotation/Controller/Responses.php b/src/Core/Annotation/Controller/Responses.php deleted file mode 100644 index 9b953bf5..00000000 --- a/src/Core/Annotation/Controller/Responses.php +++ /dev/null @@ -1,59 +0,0 @@ -getCode()])) { - $takenCodes[$response->getCode()] = $response; - } else { - throw new AnnotationException(sprintf( - 'Multiple @Response annotations with "code=%s" given. Each response must have unique code.', - $response->getCode() - )); - } - } - - $this->responses = $responses; - } - - /** - * @return Response[] - */ - public function getResponses(): array - { - return $this->responses; - } - -} diff --git a/src/Core/Annotation/Controller/Tag.php b/src/Core/Annotation/Controller/Tag.php index 0727ca5f..aa2cb6a6 100644 --- a/src/Core/Annotation/Controller/Tag.php +++ b/src/Core/Annotation/Controller/Tag.php @@ -3,15 +3,8 @@ namespace Apitte\Core\Annotation\Controller; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Common\Annotations\Annotation\Target; -use Doctrine\Common\Annotations\AnnotationException; +use InvalidArgumentException; -/** - * @Annotation - * @Target({"CLASS", "METHOD"}) - * @NamedArgumentConstructor() - */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Tag { @@ -22,7 +15,7 @@ public function __construct( ) { if ($name === '') { - throw new AnnotationException('Empty @Tag name given'); + throw new InvalidArgumentException('Empty #[Tag] name given'); } } diff --git a/src/Core/DI/Loader/DoctrineAnnotationLoader.php b/src/Core/DI/Loader/AttributeLoader.php similarity index 53% rename from src/Core/DI/Loader/DoctrineAnnotationLoader.php rename to src/Core/DI/Loader/AttributeLoader.php index 99d2c178..8985d8d0 100644 --- a/src/Core/DI/Loader/DoctrineAnnotationLoader.php +++ b/src/Core/DI/Loader/AttributeLoader.php @@ -5,32 +5,25 @@ use Apitte\Core\Annotation\Controller\Id; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Negotiation; -use Apitte\Core\Annotation\Controller\Negotiations; use Apitte\Core\Annotation\Controller\OpenApi; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\RequestBody; use Apitte\Core\Annotation\Controller\RequestParameter; -use Apitte\Core\Annotation\Controller\RequestParameters; use Apitte\Core\Annotation\Controller\Response; -use Apitte\Core\Annotation\Controller\Responses; use Apitte\Core\Annotation\Controller\Tag; -use Apitte\Core\DI\LoaderFactory\DualReaderFactory; use Apitte\Core\Exception\Logical\InvalidStateException; use Apitte\Core\Schema\Builder\Controller\Controller; use Apitte\Core\Schema\Builder\Controller\Method as SchemaMethod; use Apitte\Core\Schema\EndpointRequestBody; use Apitte\Core\Schema\SchemaBuilder; use Apitte\Core\UI\Controller\IController; -use Doctrine\Common\Annotations\Reader; use Nette\Neon\Neon; use ReflectionClass; use ReflectionMethod; -class DoctrineAnnotationLoader extends AbstractContainerLoader +class AttributeLoader extends AbstractContainerLoader { - private ?Reader $reader = null; - /** @var mixed[] */ private array $meta = [ 'services' => [], @@ -62,11 +55,11 @@ public function load(SchemaBuilder $builder): SchemaBuilder // Create scheme endpoint $schemeController = $builder->addController($type); - // Parse @Path, @ControllerId - $this->parseControllerClassAnnotations($schemeController, $class); + // Parse #[Path], #[Id] + $this->parseControllerClassAttributes($schemeController, $class); - // Parse @Method, @Path - $this->parseControllerMethodsAnnotations($schemeController, $class); + // Parse #[Method], #[Path] + $this->parseControllerMethodsAttributes($schemeController, $class); } return $builder; @@ -141,33 +134,33 @@ protected function acceptController(ReflectionClass $class): bool /** * @param ReflectionClass $class */ - protected function parseControllerClassAnnotations(Controller $controller, ReflectionClass $class): void + protected function parseControllerClassAttributes(Controller $controller, ReflectionClass $class): void { - // Read class annotations - $annotations = $this->getReader()->getClassAnnotations($class); + // Read class attributes + $attributes = $this->getClassAttributes($class); - // Iterate over all class annotations in controller - foreach ($annotations as $annotation) { - // Parse @Path ======================= - if ($annotation instanceof Path) { - $controller->setPath($annotation->getPath()); + // Iterate over all class attributes in controller + foreach ($attributes as $attribute) { + // Parse #[Path] ======================= + if ($attribute instanceof Path) { + $controller->setPath($attribute->getPath()); continue; } - // Parse @ControllerId ========================= - if ($annotation instanceof Id) { - $controller->setId($annotation->getName()); + // Parse #[Id] ========================= + if ($attribute instanceof Id) { + $controller->setId($attribute->getName()); } - // Parse @Tag ================================== - if ($annotation instanceof Tag) { - $controller->addTag($annotation->getName(), $annotation->getValue()); + // Parse #[Tag] ================================== + if ($attribute instanceof Tag) { + $controller->addTag($attribute->getName(), $attribute->getValue()); } - // Parse @OpenApi ============================ - if ($annotation instanceof OpenApi) { - $controller->setOpenApi(Neon::decode($annotation->getData()) ?? []); + // Parse #[OpenApi] ============================ + if ($attribute instanceof OpenApi) { + $controller->setOpenApi(Neon::decode($attribute->getData()) ?? []); continue; } @@ -176,26 +169,26 @@ protected function parseControllerClassAnnotations(Controller $controller, Refle // Reverse order $reversed = array_reverse($this->meta['services'][$class->getName()]['parents']); - // Iterate over all class annotations in controller's parents + // Iterate over all class attributes in controller's parents foreach ($reversed as $parent) { - // Read parent class annotations - $parentAnnotations = $this->getReader()->getClassAnnotations($parent); - - // Iterate over all parent class annotations - foreach ($parentAnnotations as $annotation) { - // Parse @GroupId ========================== - if ($annotation instanceof Id) { - $controller->addGroupId($annotation->getName()); + // Read parent class attributes + $parentAttributes = $this->getClassAttributes($parent); + + // Iterate over all parent class attributes + foreach ($parentAttributes as $attribute) { + // Parse #[Id] ========================== + if ($attribute instanceof Id) { + $controller->addGroupId($attribute->getName()); } - // Parse @Path ======================== - if ($annotation instanceof Path) { - $controller->addGroupPath($annotation->getPath()); + // Parse #[Path] ======================== + if ($attribute instanceof Path) { + $controller->addGroupPath($attribute->getPath()); } - // Parse @Tag ============================== - if ($annotation instanceof Tag) { - $controller->addTag($annotation->getName(), $annotation->getValue()); + // Parse #[Tag] ============================== + if ($attribute instanceof Tag) { + $controller->addTag($attribute->getName(), $attribute->getValue()); } } } @@ -204,125 +197,119 @@ protected function parseControllerClassAnnotations(Controller $controller, Refle /** * @param ReflectionClass $reflectionClass */ - protected function parseControllerMethodsAnnotations(Controller $controller, ReflectionClass $reflectionClass): void + protected function parseControllerMethodsAttributes(Controller $controller, ReflectionClass $reflectionClass): void { // Iterate over all methods in class foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - // Read method annotations - $annotations = $this->getReader()->getMethodAnnotations($method); + // Read method attributes + $attributes = $this->getMethodAttributes($method); - // Skip if method has no @Path/@Method annotations - if (count($annotations) <= 0) { + // Skip if method has no #[Path]/#[Method] attributes + if (count($attributes) <= 0) { continue; } // Append method to scheme $schemaMethod = $controller->addMethod($method->getName()); - // Iterate over all method annotations - foreach ($annotations as $annotation) { - // Parse @Path ============================= - if ($annotation instanceof Path) { - $schemaMethod->setPath($annotation->getPath()); - - continue; - } - - // Parse @Method =========================== - if ($annotation instanceof Method) { - $schemaMethod->addHttpMethods($annotation->getMethods()); + // Iterate over all method attributes + foreach ($attributes as $attribute) { + // Parse #[Path] ============================= + if ($attribute instanceof Path) { + $schemaMethod->setPath($attribute->getPath()); continue; } - // Parse @Tag ============================== - if ($annotation instanceof Tag) { - $schemaMethod->addTag($annotation->getName(), $annotation->getValue()); + // Parse #[Method] =========================== + if ($attribute instanceof Method) { + $schemaMethod->addHttpMethods($attribute->getMethods()); continue; } - // Parse @Id =============================== - if ($annotation instanceof Id) { - $schemaMethod->setId($annotation->getName()); + // Parse #[Tag] ============================== + if ($attribute instanceof Tag) { + $schemaMethod->addTag($attribute->getName(), $attribute->getValue()); continue; } - // Parse @RequestParameters ================ - if ($annotation instanceof RequestParameters) { - foreach ($annotation->getParameters() as $parameter) { - $this->addEndpointParameterToSchemaMethod($schemaMethod, $parameter); - } + // Parse #[Id] =============================== + if ($attribute instanceof Id) { + $schemaMethod->setId($attribute->getName()); continue; } // Parse #[RequestParameter] ================ - if ($annotation instanceof RequestParameter) { - $this->addEndpointParameterToSchemaMethod($schemaMethod, $annotation); - - continue; - } - - // Parse @Responses ================ - if ($annotation instanceof Responses) { - foreach ($annotation->getResponses() as $response) { - $this->addResponseToSchemaMethod($schemaMethod, $response); - } + if ($attribute instanceof RequestParameter) { + $this->addEndpointParameterToSchemaMethod($schemaMethod, $attribute); continue; } - // Parse #[Response] attribute - if ($annotation instanceof Response) { - $this->addResponseToSchemaMethod($schemaMethod, $annotation); + // Parse #[Response] ================ + if ($attribute instanceof Response) { + $this->addResponseToSchemaMethod($schemaMethod, $attribute); continue; } - // Parse @Request ================ - if ($annotation instanceof RequestBody) { + // Parse #[RequestBody] ================ + if ($attribute instanceof RequestBody) { $requestBody = new EndpointRequestBody(); - $requestBody->setDescription($annotation->getDescription()); - $requestBody->setEntity($annotation->getEntity()); - $requestBody->setRequired($annotation->isRequired()); - $requestBody->setValidation($annotation->isValidation()); + $requestBody->setDescription($attribute->getDescription()); + $requestBody->setEntity($attribute->getEntity()); + $requestBody->setRequired($attribute->isRequired()); + $requestBody->setValidation($attribute->isValidation()); $schemaMethod->setRequestBody($requestBody); continue; } - // Parse @OpenApi ================ - if ($annotation instanceof OpenApi) { - $schemaMethod->setOpenApi(Neon::decode($annotation->getData()) ?? []); + // Parse #[OpenApi] ================ + if ($attribute instanceof OpenApi) { + $schemaMethod->setOpenApi(Neon::decode($attribute->getData()) ?? []); continue; } - // Parse @Negotiations ===================== - if ($annotation instanceof Negotiations) { - foreach ($annotation->getNegotiations() as $negotiation) { - $this->addNegotiationToSchemaMethod($schemaMethod, $negotiation); - } - } - // Parse #[Negotiation] ===================== - if ($annotation instanceof Negotiation) { - $this->addNegotiationToSchemaMethod($schemaMethod, $annotation); + if ($attribute instanceof Negotiation) { + $this->addNegotiationToSchemaMethod($schemaMethod, $attribute); } } } } - protected function getReader(): Reader + /** + * @param ReflectionClass $class + * @return object[] + */ + protected function getClassAttributes(ReflectionClass $class): array { - if ($this->reader === null) { - $dualReaderFactory = new DualReaderFactory(); - $this->reader = $dualReaderFactory->create(); + $attributes = []; + + foreach ($class->getAttributes() as $attribute) { + $attributes[] = $attribute->newInstance(); + } + + return $attributes; + } + + /** + * @return object[] + */ + protected function getMethodAttributes(ReflectionMethod $method): array + { + $attributes = []; + + foreach ($method->getAttributes() as $attribute) { + $attributes[] = $attribute->newInstance(); } - return $this->reader; + return $attributes; } private function addEndpointParameterToSchemaMethod(SchemaMethod $schemaMethod, RequestParameter $requestParameter): void diff --git a/src/Core/DI/LoaderFactory/DualReaderFactory.php b/src/Core/DI/LoaderFactory/DualReaderFactory.php deleted file mode 100644 index 5752c2f9..00000000 --- a/src/Core/DI/LoaderFactory/DualReaderFactory.php +++ /dev/null @@ -1,30 +0,0 @@ - Expect::structure([ 'annotations' => Expect::structure([ 'enable' => Expect::bool(true), - 'loader' => Expect::string(DoctrineAnnotationLoader::class), + 'loader' => Expect::string(AttributeLoader::class), ]), 'neon' => Expect::structure([ 'enable' => Expect::bool(false), diff --git a/src/Core/Mapping/Validator/SymfonyValidator.php b/src/Core/Mapping/Validator/SymfonyValidator.php index 8fbe51b6..9d1ee541 100644 --- a/src/Core/Mapping/Validator/SymfonyValidator.php +++ b/src/Core/Mapping/Validator/SymfonyValidator.php @@ -3,8 +3,6 @@ namespace Apitte\Core\Mapping\Validator; use Apitte\Core\Exception\Api\ValidationException; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\Reader; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; use Symfony\Component\Validator\Validation; use Symfony\Contracts\Translation\TranslatorInterface; @@ -21,13 +19,6 @@ class SymfonyValidator implements IEntityValidator /** @var list|null */ private ?array $groups = null; - public function __construct( - private ?Reader $reader = null, - ) - { - AnnotationReader::addGlobalIgnoredName('mapping'); - } - public function setConstraintValidatorFactory(ConstraintValidatorFactoryInterface $constraintValidatorFactory): void { $this->constraintValidatorFactory = $constraintValidatorFactory; @@ -60,10 +51,6 @@ public function validate(object $entity): void $validatorBuilder = Validation::createValidatorBuilder(); $validatorBuilder->enableAttributeMapping(); - if (method_exists($validatorBuilder, 'setDoctrineAnnotationReader')) { - $validatorBuilder->setDoctrineAnnotationReader($this->reader); - } - if ($this->constraintValidatorFactory !== null) { $validatorBuilder->setConstraintValidatorFactory($this->constraintValidatorFactory); } diff --git a/tests/Cases/Core/Annotation/Controller/Id.phpt b/tests/Cases/Core/Annotation/Controller/Id.phpt index d482c897..3804c2d3 100644 --- a/tests/Cases/Core/Annotation/Controller/Id.phpt +++ b/tests/Cases/Core/Annotation/Controller/Id.phpt @@ -4,7 +4,6 @@ require_once __DIR__ . '/../../../../bootstrap.php'; use Apitte\Core\Annotation\Controller\Id; use Contributte\Tester\Toolkit; -use Doctrine\Common\Annotations\AnnotationException; use Tester\Assert; // Value @@ -17,5 +16,5 @@ Toolkit::test(function (): void { Toolkit::test(function (): void { Assert::exception(function (): void { new Id(''); - }, AnnotationException::class, 'Empty @Id given'); + }, InvalidArgumentException::class, 'Empty #[Id] given'); }); diff --git a/tests/Cases/Core/Annotation/Controller/Method.phpt b/tests/Cases/Core/Annotation/Controller/Method.phpt index e7d55176..05214cdd 100644 --- a/tests/Cases/Core/Annotation/Controller/Method.phpt +++ b/tests/Cases/Core/Annotation/Controller/Method.phpt @@ -2,7 +2,6 @@ use Apitte\Core\Annotation\Controller\Method; use Contributte\Tester\Toolkit; -use Doctrine\Common\Annotations\AnnotationException; use Tester\Assert; require_once __DIR__ . '/../../../../bootstrap.php'; @@ -20,9 +19,9 @@ Toolkit::test(function (): void { Toolkit::test(function (): void { Assert::exception(function (): void { new Method([]); - }, AnnotationException::class, 'Empty @Method given'); + }, InvalidArgumentException::class, 'Empty #[Method] given'); Assert::exception(function (): void { new Method(''); - }, AnnotationException::class, 'Empty @Method given'); + }, InvalidArgumentException::class, 'Empty #[Method] given'); }); diff --git a/tests/Cases/Core/Annotation/Controller/Negotiations.phpt b/tests/Cases/Core/Annotation/Controller/Negotiations.phpt deleted file mode 100644 index cad27528..00000000 --- a/tests/Cases/Core/Annotation/Controller/Negotiations.phpt +++ /dev/null @@ -1,27 +0,0 @@ -getNegotiations()); -}); - -// Exception - empty negotiations -Toolkit::test(function (): void { - Assert::exception(function (): void { - new Negotiations([]); - }, AnnotationException::class, 'Empty @Negotiations given'); -}); diff --git a/tests/Cases/Core/Annotation/Controller/Path.phpt b/tests/Cases/Core/Annotation/Controller/Path.phpt index 6de23722..61dee9c8 100644 --- a/tests/Cases/Core/Annotation/Controller/Path.phpt +++ b/tests/Cases/Core/Annotation/Controller/Path.phpt @@ -4,7 +4,6 @@ require_once __DIR__ . '/../../../../bootstrap.php'; use Apitte\Core\Annotation\Controller\Path; use Contributte\Tester\Toolkit; -use Doctrine\Common\Annotations\AnnotationException; use Tester\Assert; // OK @@ -17,5 +16,5 @@ Toolkit::test(function (): void { Toolkit::test(function (): void { Assert::exception(function (): void { new Path(''); - }, AnnotationException::class, 'Empty @Path given'); + }, InvalidArgumentException::class, 'Empty #[Path] given'); }); diff --git a/tests/Cases/Core/Annotation/Controller/RequestParameter.phpt b/tests/Cases/Core/Annotation/Controller/RequestParameter.phpt index 595911c8..ae852026 100644 --- a/tests/Cases/Core/Annotation/Controller/RequestParameter.phpt +++ b/tests/Cases/Core/Annotation/Controller/RequestParameter.phpt @@ -5,7 +5,6 @@ require_once __DIR__ . '/../../../../bootstrap.php'; use Apitte\Core\Annotation\Controller\RequestParameter; use Apitte\Core\Schema\EndpointParameter; use Contributte\Tester\Toolkit; -use Doctrine\Common\Annotations\AnnotationException; use Tester\Assert; // OK @@ -49,12 +48,12 @@ Toolkit::test(function (): void { Toolkit::test(function (): void { Assert::exception(function (): void { new RequestParameter('Param', '', EndpointParameter::IN_PATH); - }, AnnotationException::class, 'Empty @RequestParameter type given'); + }, InvalidArgumentException::class, 'Empty #[RequestParameter] type given'); }); // Exception - invalid parameter location Toolkit::test(function (): void { Assert::exception(function (): void { new RequestParameter('Param', EndpointParameter::TYPE_STRING, 'invalid'); - }, AnnotationException::class, 'Invalid @RequestParameter in given'); + }, InvalidArgumentException::class, 'Invalid #[RequestParameter] in given'); }); diff --git a/tests/Cases/Core/Annotation/Controller/RequestParameters.phpt b/tests/Cases/Core/Annotation/Controller/RequestParameters.phpt deleted file mode 100644 index e55f42bb..00000000 --- a/tests/Cases/Core/Annotation/Controller/RequestParameters.phpt +++ /dev/null @@ -1,51 +0,0 @@ -getParameters()); -}); - -// Exception - empty negotiations -Toolkit::test(function (): void { - Assert::exception(function (): void { - new RequestParameters([]); - }, AnnotationException::class, 'Empty @RequestParameters given'); -}); - -// Exception - multiple parameters with same name and location -Toolkit::test(function (): void { - Assert::exception( - function (): void { - new RequestParameters([ - $parameter1 = new RequestParameter( - 'foo', - EndpointParameter::TYPE_STRING, - EndpointParameter::IN_QUERY, - false - ), - $parameter2 = new RequestParameter( - 'foo', - EndpointParameter::TYPE_INTEGER, - EndpointParameter::IN_QUERY, - false - )]); - }, - AnnotationException::class, - 'Multiple @RequestParameter annotations with "name=foo" and "in=query" given. Each parameter must have unique combination of location and name.' - ); -}); diff --git a/tests/Cases/Core/Annotation/Controller/Tag.phpt b/tests/Cases/Core/Annotation/Controller/Tag.phpt index 66e4935c..c2782ee6 100644 --- a/tests/Cases/Core/Annotation/Controller/Tag.phpt +++ b/tests/Cases/Core/Annotation/Controller/Tag.phpt @@ -4,7 +4,6 @@ require_once __DIR__ . '/../../../../bootstrap.php'; use Apitte\Core\Annotation\Controller\Tag; use Contributte\Tester\Toolkit; -use Doctrine\Common\Annotations\AnnotationException; use Tester\Assert; // OK @@ -19,5 +18,5 @@ Toolkit::test(function (): void { Toolkit::test(function (): void { Assert::exception(function (): void { new Tag('', null); - }, AnnotationException::class, 'Empty @Tag name given'); + }, InvalidArgumentException::class, 'Empty #[Tag] name given'); }); diff --git a/tests/Cases/Core/DI/ApiExtension.phpt b/tests/Cases/Core/DI/ApiExtension.phpt index f0215c9f..38277137 100644 --- a/tests/Cases/Core/DI/ApiExtension.phpt +++ b/tests/Cases/Core/DI/ApiExtension.phpt @@ -42,7 +42,7 @@ Toolkit::test(function (): void { Assert::type(Application::class, $container->getService('api.core.application')); }); -// Annotations +// Attributes Toolkit::test(function (): void { $loader = new ContainerLoader(Environment::getTestDir(), true); $class = $loader->load(function (Compiler $compiler): void { @@ -102,9 +102,7 @@ Toolkit::test(function (): void { $compiler->addConfig(Neonkit::load(<<<'NEON' services: validator: - factory: Apitte\Core\Mapping\Validator\SymfonyValidator( - Doctrine\Common\Annotations\AnnotationReader() - ) + factory: Apitte\Core\Mapping\Validator\SymfonyValidator() setup: - setConstraintValidatorFactory(Symfony\Component\Validator\ConstraintValidatorFactory()) - setTranslator(Symfony\Component\Translation\Translator(en)) diff --git a/tests/Cases/Core/DI/Loader/DoctrineAnnotationLoader.phpt b/tests/Cases/Core/DI/Loader/AttributeLoader.phpt similarity index 87% rename from tests/Cases/Core/DI/Loader/DoctrineAnnotationLoader.phpt rename to tests/Cases/Core/DI/Loader/AttributeLoader.phpt index 13627d94..5e00e9af 100644 --- a/tests/Cases/Core/DI/Loader/DoctrineAnnotationLoader.phpt +++ b/tests/Cases/Core/DI/Loader/AttributeLoader.phpt @@ -2,7 +2,7 @@ require_once __DIR__ . '/../../../../bootstrap.php'; -use Apitte\Core\DI\Loader\DoctrineAnnotationLoader; +use Apitte\Core\DI\Loader\AttributeLoader; use Apitte\Core\Schema\Builder\Controller\Controller; use Apitte\Core\Schema\SchemaBuilder; use Apitte\Core\UI\Controller\IController; @@ -28,7 +28,7 @@ Toolkit::test(function (): void { return $controllers; }); - $loader = new DoctrineAnnotationLoader($builder); + $loader = new AttributeLoader($builder); $schemaBuilder = $loader->load(new SchemaBuilder()); Assert::type(SchemaBuilder::class, $schemaBuilder); @@ -36,19 +36,16 @@ Toolkit::test(function (): void { Mockery::close(); }); -// Parse annotations +// Parse attributes Toolkit::test(function (): void { $builder = new ContainerBuilder(); $builder->addDefinition('annotation_controller') ->setType(AnnotationFoobarController::class); - // include attribute controller only on PHP 8.0 and up - if (PHP_VERSION_ID >= 80000) { - $builder->addDefinition('attribute_controller') - ->setType(AttributeFoobarController::class); - } + $builder->addDefinition('attribute_controller') + ->setType(AttributeFoobarController::class); - $loader = new DoctrineAnnotationLoader($builder); + $loader = new AttributeLoader($builder); $schemaBuilder = $loader->load(new SchemaBuilder()); Assert::type(SchemaBuilder::class, $schemaBuilder); diff --git a/tests/Cases/Core/DI/Loader/DoctrineMultiAnnotationLoader.phpt b/tests/Cases/Core/DI/Loader/AttributeMultiLoader.phpt similarity index 91% rename from tests/Cases/Core/DI/Loader/DoctrineMultiAnnotationLoader.phpt rename to tests/Cases/Core/DI/Loader/AttributeMultiLoader.phpt index 184387b3..7a055c4b 100644 --- a/tests/Cases/Core/DI/Loader/DoctrineMultiAnnotationLoader.phpt +++ b/tests/Cases/Core/DI/Loader/AttributeMultiLoader.phpt @@ -2,7 +2,7 @@ require_once __DIR__ . '/../../../../bootstrap.php'; -use Apitte\Core\DI\Loader\DoctrineAnnotationLoader; +use Apitte\Core\DI\Loader\AttributeLoader; use Apitte\Core\Schema\Builder\Controller\Controller; use Apitte\Core\Schema\EndpointParameter; use Apitte\Core\Schema\SchemaBuilder; @@ -12,19 +12,16 @@ use Tester\Assert; use Tests\Fixtures\Controllers\AnnotationMultiController; use Tests\Fixtures\Controllers\AttributeMultiController; -// Parse annotations +// Parse attributes Toolkit::test(function (): void { $builder = new ContainerBuilder(); $builder->addDefinition('annotation_multi_controller') ->setType(AnnotationMultiController::class); - // include attribute controller only on PHP 8.0 and up - if (PHP_VERSION_ID >= 80000) { - $builder->addDefinition('attribute_multi_controller') - ->setType(AttributeMultiController::class); - } + $builder->addDefinition('attribute_multi_controller') + ->setType(AttributeMultiController::class); - $loader = new DoctrineAnnotationLoader($builder); + $loader = new AttributeLoader($builder); $schemaBuilder = $loader->load(new SchemaBuilder()); Assert::type(SchemaBuilder::class, $schemaBuilder); diff --git a/tests/Cases/Core/DI/Loader/MixedAttributeAnnotationLoader.phpt b/tests/Cases/Core/DI/Loader/MixedAttributeLoader.phpt similarity index 90% rename from tests/Cases/Core/DI/Loader/MixedAttributeAnnotationLoader.phpt rename to tests/Cases/Core/DI/Loader/MixedAttributeLoader.phpt index c76759a1..4a1bcb7d 100644 --- a/tests/Cases/Core/DI/Loader/MixedAttributeAnnotationLoader.phpt +++ b/tests/Cases/Core/DI/Loader/MixedAttributeLoader.phpt @@ -2,7 +2,7 @@ require_once __DIR__ . '/../../../../bootstrap.php'; -use Apitte\Core\DI\Loader\DoctrineAnnotationLoader; +use Apitte\Core\DI\Loader\AttributeLoader; use Apitte\Core\Schema\SchemaBuilder; use Apitte\Core\Schema\Validation\RequestParameterValidation; use Contributte\Tester\Toolkit; @@ -12,7 +12,7 @@ use Tests\Fixtures\Controllers\Mixed\AnnotationAttributeController; use Tests\Fixtures\Controllers\Mixed\AttributesOnlyController; use Tests\Fixtures\Controllers\Mixed\PathAndRequestParamsController; -// Parse annotations +// Parse attributes Toolkit::test(function (): void { $builder = new ContainerBuilder(); $builder->addDefinition('first_controller') @@ -24,7 +24,7 @@ Toolkit::test(function (): void { $builder->addDefinition('third_controller') ->setType(AttributesOnlyController::class); - $loader = new DoctrineAnnotationLoader($builder); + $loader = new AttributeLoader($builder); $schemaBuilder = $loader->load(new SchemaBuilder()); Assert::type(SchemaBuilder::class, $schemaBuilder); diff --git a/tests/Cases/Core/Mapping/Validator/SymfonyValidator.phpt b/tests/Cases/Core/Mapping/Validator/SymfonyValidator.phpt index d80d1212..aa178a5b 100644 --- a/tests/Cases/Core/Mapping/Validator/SymfonyValidator.phpt +++ b/tests/Cases/Core/Mapping/Validator/SymfonyValidator.phpt @@ -5,13 +5,12 @@ require_once __DIR__ . '/../../../../bootstrap.php'; use Apitte\Core\Exception\Api\ValidationException; use Apitte\Core\Mapping\Validator\SymfonyValidator; use Contributte\Tester\Toolkit; -use Doctrine\Common\Annotations\AnnotationReader; use Tester\Assert; use Tests\Fixtures\Mapping\Validator\SimpleEntity; // Happy case Toolkit::test(function (): void { - $validator = new SymfonyValidator(new AnnotationReader()); + $validator = new SymfonyValidator(); $entity = (new SimpleEntity())->factory([ 'id' => 1, @@ -31,7 +30,7 @@ Toolkit::test(function (): void { // Invalid value Toolkit::test(function (): void { - $validator = new SymfonyValidator(new AnnotationReader()); + $validator = new SymfonyValidator(); $entity = (new SimpleEntity())->factory([ 'id' => 1, @@ -54,7 +53,7 @@ Toolkit::test(function (): void { } }); -// Without annotation reader +// Attributes-only validation Toolkit::test(function (): void { $validator = new SymfonyValidator(); diff --git a/tests/Fixtures/Controllers/AbstractController.php b/tests/Fixtures/Controllers/AbstractController.php index cdaaba43..08f53a5c 100644 --- a/tests/Fixtures/Controllers/AbstractController.php +++ b/tests/Fixtures/Controllers/AbstractController.php @@ -6,10 +6,8 @@ use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\UI\Controller\IController; -/** - * @Id("testapi") - * @Path("/api") - */ +#[Id('testapi')] +#[Path('/api')] abstract class AbstractController implements IController { diff --git a/tests/Fixtures/Controllers/AnnotationFoobarController.php b/tests/Fixtures/Controllers/AnnotationFoobarController.php index 69a2200a..34fe7df2 100644 --- a/tests/Fixtures/Controllers/AnnotationFoobarController.php +++ b/tests/Fixtures/Controllers/AnnotationFoobarController.php @@ -6,54 +6,45 @@ use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; -/** - * @Apitte\Path("/foobar") - * @Apitte\Id("foobar") - */ +#[Apitte\Path('/foobar')] +#[Apitte\Id('foobar')] final class AnnotationFoobarController extends ApiV1Controller { - /** - * @Apitte\Path("/baz1") - * @Apitte\Method("GET") - * @Apitte\Id("baz1") - */ + #[Apitte\Path('/baz1')] + #[Apitte\Method('GET')] + #[Apitte\Id('baz1')] public function baz1(ApiRequest $request, ApiResponse $response): void { // Tests } - /** - * @Apitte\Path("/baz2") - * @Apitte\Method({"GET", "POST"}) - */ + #[Apitte\Path('/baz2')] + #[Apitte\Method(['GET', 'POST'])] public function baz2(ApiRequest $request, ApiResponse $response): void { // Tests } - /** - * @Apitte\Path("/baz2") - * @Apitte\Method({"PUT"}) - */ + #[Apitte\Path('/baz2')] + #[Apitte\Method(['PUT'])] public function baz3(ApiRequest $request, ApiResponse $response): void { // Tests } - /** - * @Apitte\Path("/openapi") - * @Apitte\Method({"PUT"}) - * @Apitte\OpenApi(" - * foo: - * bar: baz - * lorem: - * - ipsum - * - dolor - * - sit - * - amet - * ") - */ + #[Apitte\Path('/openapi')] + #[Apitte\Method(['PUT'])] + #[Apitte\OpenApi(<<<'NEON' + foo: + bar: baz + lorem: + - ipsum + - dolor + - sit + - amet + NEON + )] public function openapi(ApiRequest $request, ApiResponse $response): void { // Tests diff --git a/tests/Fixtures/Controllers/AnnotationMultiController.php b/tests/Fixtures/Controllers/AnnotationMultiController.php index 666acf95..3d5bdfd3 100644 --- a/tests/Fixtures/Controllers/AnnotationMultiController.php +++ b/tests/Fixtures/Controllers/AnnotationMultiController.php @@ -3,48 +3,31 @@ namespace Tests\Fixtures\Controllers; use Apitte\Core\Annotation\Controller\Negotiation; -use Apitte\Core\Annotation\Controller\Negotiations; use Apitte\Core\Annotation\Controller\RequestParameter; -use Apitte\Core\Annotation\Controller\RequestParameters; use Apitte\Core\Annotation\Controller\Response; -use Apitte\Core\Annotation\Controller\Responses; use Apitte\Core\Annotation\Controller\Tag; -/** - * @Tag("nice", value="yes") - * @Tag("one", value="no") - */ +#[Tag('nice', value: 'yes')] +#[Tag('one', value: 'no')] final class AnnotationMultiController extends ApiV1Controller { - /** - * @Responses({ - * @Response("some_description", code="cz"), - * @Response(description="some_description_2", code="com") - * }) - */ + #[Response('some_description', code: 'cz')] + #[Response(description: 'some_description_2', code: 'com')] public function responses(): void { // Tests } - /** - * @RequestParameters({ - * @RequestParameter(name="name_value", type="type_value", in="path"), - * @RequestParameter(in="query", type="type_value_2", name="name_value_2") - * }) - */ + #[RequestParameter(name: 'name_value', type: 'type_value', in: 'path')] + #[RequestParameter(in: 'query', type: 'type_value_2', name: 'name_value_2')] public function requestParameters(): void { // Tests } - /** - * @Negotiations({ - * @Negotiation("some_suffix"), - * @Negotiation(suffix="some_suffix_2") - * }) - */ + #[Negotiation('some_suffix')] + #[Negotiation(suffix: 'some_suffix_2')] public function negotiations(): void { // Tests diff --git a/tests/Fixtures/Controllers/ApiV1Controller.php b/tests/Fixtures/Controllers/ApiV1Controller.php index 73803ccc..a1795d24 100644 --- a/tests/Fixtures/Controllers/ApiV1Controller.php +++ b/tests/Fixtures/Controllers/ApiV1Controller.php @@ -4,9 +4,7 @@ use Apitte\Core\Annotation\Controller\Path; -/** - * @Path("/v1") - */ +#[Path('/v1')] abstract class ApiV1Controller extends AbstractController { diff --git a/tests/Fixtures/Controllers/Mixed/AnnotationAttributeController.php b/tests/Fixtures/Controllers/Mixed/AnnotationAttributeController.php index 4769a747..46628f89 100644 --- a/tests/Fixtures/Controllers/Mixed/AnnotationAttributeController.php +++ b/tests/Fixtures/Controllers/Mixed/AnnotationAttributeController.php @@ -4,17 +4,12 @@ use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\RequestParameter; -use Apitte\Core\Annotation\Controller\RequestParameters; use Tests\Fixtures\Controllers\ApiV1Controller; final class AnnotationAttributeController extends ApiV1Controller { - /** - * @RequestParameters({ - * @RequestParameter(in="path", type="int", name="userId", description="User ID"), - * }) - */ + #[RequestParameter(name: 'userId', type: 'int', in: 'path', description: 'User ID')] #[Path(path: '/user/{userId}/collections')] public function run(): void { diff --git a/tests/Fixtures/Controllers/Mixed/PathAndRequestParamsController.php b/tests/Fixtures/Controllers/Mixed/PathAndRequestParamsController.php index e281fe61..dfa90206 100644 --- a/tests/Fixtures/Controllers/Mixed/PathAndRequestParamsController.php +++ b/tests/Fixtures/Controllers/Mixed/PathAndRequestParamsController.php @@ -4,18 +4,13 @@ use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\RequestParameter; -use Apitte\Core\Annotation\Controller\RequestParameters; use Tests\Fixtures\Controllers\ApiV1Controller; final class PathAndRequestParamsController extends ApiV1Controller { - /** - * @RequestParameters({ - * @RequestParameter(name="userId", type="int", description="User ID", in="path"), - * @RequestParameter(name="photoId", type="int", description="Verification photo ID", in="path") - * }) - */ + #[RequestParameter(name: 'userId', type: 'int', description: 'User ID', in: 'path')] + #[RequestParameter(name: 'photoId', type: 'int', description: 'Verification photo ID', in: 'path')] #[Path(path: '/user/{userId}/verification-photo/{photoId}')] public function run(): void {