From 6c5ac8509bb49b4cecd7f9b523023b9b80b7603a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Ondr=C3=A1=C4=8Dek?= Date: Sun, 7 Dec 2025 15:16:52 +0100 Subject: [PATCH 1/2] Add missing generics, use reflections instead of variable property access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Roman Ondráček --- phpstan.neon | 16 ------- .../DI/Loader/DoctrineAnnotationLoader.php | 16 ++++++- src/Core/Mapping/Request/AbstractEntity.php | 9 +++- src/Core/Mapping/Request/BasicEntity.php | 45 ++++++++++++++----- src/Core/Mapping/Response/AbstractEntity.php | 9 +++- src/Core/Mapping/Response/BasicEntity.php | 9 +++- src/Core/Mapping/TReflectionProperties.php | 11 ++++- src/Core/Mapping/Validator/BasicValidator.php | 20 ++++++++- src/Negotiation/Http/ArrayEntity.php | 13 +++--- .../SchemaDefinition/Entity/EntityAdapter.php | 10 +++-- 10 files changed, 112 insertions(+), 46 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index e0672698..de32583f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,17 +10,8 @@ parameters: - .docs reportMaybesInPropertyPhpDocTypes: false - checkGenericClassInNonGenericObjectType: false ignoreErrors: - # Intended property access - required for reflection - - '#^Variable property access on (\$this|static)\(Apitte\\Core\\Mapping\\(Request|Response)\\BasicEntity\)\.$#' - - '#^Variable property access on Apitte\\Core\\Mapping\\Request\\BasicEntity\.$#' - - # Phpstan bug - - message: '#^Parameter \#1 \$argument of class ReflectionClass constructor expects class-string|T of object, string given\.$#' - path: %currentWorkingDirectory%/src/Core/DI/Loader/DoctrineAnnotationLoader.php - # Missing strict comparison - '#^Only booleans are allowed in#' @@ -48,13 +39,6 @@ parameters: # This should not happen because null is returned on error - '#Method Apitte\\Core\\Utils\\Helpers::slashless\(\) should return string but returns string\|null\.#' - # To pass php8.0 tests where library doesn't have isSingle() - - message: """ - #^Call to deprecated method isSingle\\(\\) of class Nette\\\\Utils\\\\Type\\: - use isSimple\\(\\)$# - """ - path: %currentWorkingDirectory%/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php - # Nette changed return typehint - message: "#^Method Apitte\\\\OpenApi\\\\SchemaDefinition\\\\Entity\\\\EntityAdapter\\:\\:getNativePropertyType\\(\\) should return string but returns array\\\\|string\\.$#" path: %currentWorkingDirectory%/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php diff --git a/src/Core/DI/Loader/DoctrineAnnotationLoader.php b/src/Core/DI/Loader/DoctrineAnnotationLoader.php index 75413e1c..41b30e37 100644 --- a/src/Core/DI/Loader/DoctrineAnnotationLoader.php +++ b/src/Core/DI/Loader/DoctrineAnnotationLoader.php @@ -43,6 +43,7 @@ public function load(SchemaBuilder $builder): SchemaBuilder // Iterate over all controllers foreach ($controllers as $def) { + /** @var class-string|null $type */ $type = $def->getType(); if ($type === null) { @@ -71,6 +72,10 @@ public function load(SchemaBuilder $builder): SchemaBuilder return $builder; } + /** + * @param class-string $class + * @return ReflectionClass + */ protected function analyseClass(string $class): ReflectionClass { // Analyse only new-ones @@ -88,7 +93,7 @@ protected function analyseClass(string $class): ReflectionClass ]; // Get all parents - /** @var string[] $parents */ + /** @var class-string[] $parents */ $parents = class_parents($class); $reflections = []; @@ -122,11 +127,17 @@ protected function analyseClass(string $class): ReflectionClass return $classRef; } + /** + * @param ReflectionClass $class + */ protected function acceptController(ReflectionClass $class): bool { return is_subclass_of($class->getName(), IController::class); } + /** + * @param ReflectionClass $class + */ protected function parseControllerClassAnnotations(Controller $controller, ReflectionClass $class): void { // Read class annotations @@ -187,6 +198,9 @@ protected function parseControllerClassAnnotations(Controller $controller, Refle } } + /** + * @param ReflectionClass $reflectionClass + */ protected function parseControllerMethodsAnnotations(Controller $controller, ReflectionClass $reflectionClass): void { // Iterate over all methods in class diff --git a/src/Core/Mapping/Request/AbstractEntity.php b/src/Core/Mapping/Request/AbstractEntity.php index aaa4681f..5e0c7c8b 100644 --- a/src/Core/Mapping/Request/AbstractEntity.php +++ b/src/Core/Mapping/Request/AbstractEntity.php @@ -5,16 +5,21 @@ use ArrayIterator; use IteratorAggregate; +/** + * @template TKey of string|int + * @template TValue of mixed + * @implements IteratorAggregate + */ abstract class AbstractEntity implements IRequestEntity, IteratorAggregate { /** - * @return mixed[] + * @return array */ abstract public function toArray(): array; /** - * @return ArrayIterator + * @return ArrayIterator */ public function getIterator(): ArrayIterator { diff --git a/src/Core/Mapping/Request/BasicEntity.php b/src/Core/Mapping/Request/BasicEntity.php index c7074edc..b2ba5189 100644 --- a/src/Core/Mapping/Request/BasicEntity.php +++ b/src/Core/Mapping/Request/BasicEntity.php @@ -9,6 +9,11 @@ use Nette\Utils\JsonException; use TypeError; +/** + * @template TKey of string|int + * @template TValue of mixed + * @extends AbstractEntity + */ abstract class BasicEntity extends AbstractEntity { @@ -23,7 +28,7 @@ public function getRequestProperties(): array } /** - * @return BasicEntity|null + * @return BasicEntity|null */ public function fromRequest(ApiRequest $request): ?IRequestEntity { @@ -39,8 +44,8 @@ public function fromRequest(ApiRequest $request): ?IRequestEntity } /** - * @param array $data - * @return static + * @param array $data + * @return static */ public function factory(array $data): self { @@ -49,20 +54,35 @@ public function factory(array $data): self // Fill properties with real data $properties = $inst->getRequestProperties(); foreach ($properties as $property) { - if (!array_key_exists($property['name'], $data)) { + /** @var TKey $propName */ + $propName = $property['name']; + if (!array_key_exists($propName, $data)) { continue; } - $value = $data[$property['name']]; + $value = $data[$propName]; // Normalize & convert value (only not null values) if ($value !== null) { - $value = $this->normalize($property['name'], $value); + $value = $this->normalize($propName, $value); } // Fill single property try { - $inst->{$property['name']} = $value; + $propNameStr = (string) $propName; + if (property_exists($inst, $propNameStr)) { + $ref = new \ReflectionProperty($inst, $propNameStr); + $wasAccessible = $ref->isPublic(); + if (!$wasAccessible) { + $ref->setAccessible(true); + } + $ref->setValue($inst, $value); + if (!$wasAccessible) { + $ref->setAccessible(false); + } + } elseif (method_exists($inst, '__set')) { + $inst->__set($propName, $value); + } } catch (TypeError) { // do nothing, entity will be invalid if something is missing and ValidationException will be thrown } @@ -71,13 +91,18 @@ public function factory(array $data): self return $inst; } - protected function normalize(string $property, mixed $value): mixed + /** + * @param TKey $property + * @param TValue $value + * @return TValue + */ + protected function normalize(int|string $property, mixed $value): mixed { return $value; } /** - * @return static + * @return static */ protected function fromBodyRequest(ApiRequest $request): self { @@ -91,7 +116,7 @@ protected function fromBodyRequest(ApiRequest $request): self } /** - * @return static + * @return static */ protected function fromGetRequest(ApiRequest $request): self { diff --git a/src/Core/Mapping/Response/AbstractEntity.php b/src/Core/Mapping/Response/AbstractEntity.php index d18027c4..eb75ec8c 100644 --- a/src/Core/Mapping/Response/AbstractEntity.php +++ b/src/Core/Mapping/Response/AbstractEntity.php @@ -5,16 +5,21 @@ use ArrayIterator; use IteratorAggregate; +/** + * @template TKey of string|int + * @template TValue of mixed + * @implements IteratorAggregate + */ abstract class AbstractEntity implements IResponseEntity, IteratorAggregate { /** - * @return mixed[] + * @return array */ abstract public function toArray(): array; /** - * @return ArrayIterator + * @return ArrayIterator */ public function getIterator(): ArrayIterator { diff --git a/src/Core/Mapping/Response/BasicEntity.php b/src/Core/Mapping/Response/BasicEntity.php index d4ae8ba6..8a116778 100644 --- a/src/Core/Mapping/Response/BasicEntity.php +++ b/src/Core/Mapping/Response/BasicEntity.php @@ -4,13 +4,18 @@ use Apitte\Core\Mapping\TReflectionProperties; +/** + * @template TKey of string|int + * @template TValue of mixed + * @extends AbstractEntity + */ abstract class BasicEntity extends AbstractEntity { use TReflectionProperties; /** - * @return mixed[] + * @return array> */ public function getResponseProperties(): array { @@ -18,7 +23,7 @@ public function getResponseProperties(): array } /** - * @return mixed[] + * @return array */ public function toResponse(): array { diff --git a/src/Core/Mapping/TReflectionProperties.php b/src/Core/Mapping/TReflectionProperties.php index fa834506..886df0c5 100644 --- a/src/Core/Mapping/TReflectionProperties.php +++ b/src/Core/Mapping/TReflectionProperties.php @@ -53,13 +53,20 @@ public function toArray(): array { $data = []; $properties = $this->getProperties(); + $rf = new ReflectionObject($this); foreach ($properties as $property) { - if (!isset($this->{$property['name']})) { + if (!$rf->hasProperty($property['name'])) { continue; } - $data[$property['name']] = $this->{$property['name']}; + $propRf = $rf->getProperty($property['name']); + + if (!$propRf->isInitialized($this)) { + continue; + } + + $data[$property['name']] = $propRf->getValue($this); } return $data; diff --git a/src/Core/Mapping/Validator/BasicValidator.php b/src/Core/Mapping/Validator/BasicValidator.php index a3aa55fb..d987695b 100644 --- a/src/Core/Mapping/Validator/BasicValidator.php +++ b/src/Core/Mapping/Validator/BasicValidator.php @@ -25,6 +25,7 @@ public function validate(object $entity): void if ($violations !== []) { $fields = []; + foreach ($violations as $property => $messages) { $fields[$property] = count($messages) > 1 ? $messages : $messages[0]; } @@ -35,6 +36,7 @@ public function validate(object $entity): void } /** + * @param BasicEntity $entity * @return string[][] */ protected function validateProperties(BasicEntity $entity): array @@ -47,8 +49,22 @@ protected function validateProperties(BasicEntity $entity): array $propertyRf = $rf->getProperty($propertyName); $doc = (string) $propertyRf->getDocComment(); - if (str_contains($doc, '@required') && $entity->{$propertyName} === null) { - $violations[$propertyName][] = 'This value should not be null.'; + if (str_contains($doc, '@required')) { + $wasAccessible = $propertyRf->isPublic(); + + if (!$wasAccessible) { + $propertyRf->setAccessible(true); + } + + $value = $propertyRf->getValue($entity); + + if (!$wasAccessible) { + $propertyRf->setAccessible(false); + } + + if ($value === null) { + $violations[$propertyName][] = 'This value should not be null.'; + } } } diff --git a/src/Negotiation/Http/ArrayEntity.php b/src/Negotiation/Http/ArrayEntity.php index 3b8b014c..b12e754c 100644 --- a/src/Negotiation/Http/ArrayEntity.php +++ b/src/Negotiation/Http/ArrayEntity.php @@ -8,13 +8,14 @@ /** * @template TKey of string|int - * @implements IteratorAggregate + * @template TValue of mixed + * @implements IteratorAggregate */ class ArrayEntity extends AbstractEntity implements IteratorAggregate, Countable { /** - * @param mixed[] $data + * @param array $data */ public function __construct(array $data) { @@ -22,8 +23,8 @@ public function __construct(array $data) } /** - * @param mixed[] $data - * @phpstan-return static + * @param array $data + * @return static */ public static function from(array $data): static { @@ -31,7 +32,7 @@ public static function from(array $data): static } /** - * @return mixed[] + * @return array */ public function toArray(): array { @@ -39,7 +40,7 @@ public function toArray(): array } /** - * @return ArrayIterator + * @return ArrayIterator */ public function getIterator(): ArrayIterator { diff --git a/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php b/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php index bcb2e832..70cb408c 100644 --- a/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php +++ b/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php @@ -55,6 +55,7 @@ public function getMetadata(string $type): array } $resolvedTypes = []; + foreach ($types as $subType) { $resolvedTypes[] = $this->getMetadata($subType); } @@ -203,8 +204,10 @@ protected function normalizeType(string $type): string private function getPropertyType(ReflectionProperty $property): ?string { $nativeType = null; + if (PHP_VERSION_ID >= 70400 && ($type = Type::fromReflection($property)) !== null) { $nativeType = $this->getNativePropertyType($type, $property); + // If type is array/mixed or union/intersection of it, try to get more information from annotations if (!preg_match('#[|&]?(array|mixed)[|&]?#', $nativeType)) { return $nativeType; @@ -249,7 +252,7 @@ private function getPropertyType(ReflectionProperty $property): ?string } /** - * @param ReflectionClass|ReflectionClassConstant|ReflectionProperty|ReflectionFunctionAbstract $ref + * @param ReflectionClass|ReflectionClassConstant|ReflectionProperty|ReflectionFunctionAbstract $ref */ private function parseAnnotation(Reflector $ref, string $name): ?string { @@ -258,6 +261,7 @@ private function parseAnnotation(Reflector $ref, string $name): ?string } $re = '#[\s*]@' . preg_quote($name, '#') . '(?=\s|$)(?:[ \t]+([^@\s]\S*))?#'; + if ($ref->getDocComment() && preg_match($re, trim($ref->getDocComment(), '/*'), $m)) { return $m[1] ?? null; } @@ -267,11 +271,11 @@ private function parseAnnotation(Reflector $ref, string $name): ?string private function getNativePropertyType(Type $type, ReflectionProperty $property): string { - if ($type->isSingle() && count($type->getNames()) === 1) { + if ($type->isSimple() && count($type->getNames()) === 1) { return $type->getNames()[0]; } - if ($type->isUnion() || ($type->isSingle() && count($type->getNames()) === 2) // nullable type is single but returns name of type and null in names + if ($type->isUnion() || ($type->isSimple() && count($type->getNames()) === 2) // nullable type is single but returns name of type and null in names ) { return implode('|', $type->getNames()); } From 067e2cc18e13ac9c067dff68e41cd017206ec5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Ondr=C3=A1=C4=8Dek?= Date: Mon, 8 Dec 2025 11:15:18 +0100 Subject: [PATCH 2/2] Modernize code base and fix issues reported by linters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Roman Ondráček --- .github/workflows/tests.yml | 8 +- composer.json | 12 +-- phpstan.neon | 4 + ruleset.xml | 4 +- src/Console/Command/RouteDumpCommand.php | 10 +-- src/Core/Annotation/Controller/Id.php | 8 +- src/Core/Annotation/Controller/Method.php | 4 +- .../Annotation/Controller/Negotiation.php | 15 ++-- .../Annotation/Controller/Negotiations.php | 2 +- src/Core/Annotation/Controller/OpenApi.php | 2 +- src/Core/Annotation/Controller/Path.php | 8 +- .../Annotation/Controller/RequestBody.php | 19 ++--- .../Controller/RequestParameter.php | 42 ++--------- .../Controller/RequestParameters.php | 2 +- src/Core/Annotation/Controller/Response.php | 16 ++-- src/Core/Annotation/Controller/Responses.php | 2 +- src/Core/Annotation/Controller/Tag.php | 12 +-- src/Core/Application/Application.php | 9 +-- src/Core/Application/BaseApplication.php | 8 +- .../DI/Loader/AbstractContainerLoader.php | 7 +- .../DI/Loader/DoctrineAnnotationLoader.php | 5 +- src/Core/DI/Loader/NeonLoader.php | 8 +- .../DI/LoaderFactory/DualReaderFactory.php | 1 + src/Core/DI/Plugin/CoreDecoratorPlugin.php | 3 + src/Core/DI/Plugin/CoreSchemaPlugin.php | 9 ++- src/Core/DI/Plugin/Plugin.php | 8 +- src/Core/DI/Plugin/PluginCompiler.php | 11 +-- src/Core/Decorator/RequestEntityDecorator.php | 7 +- .../Decorator/RequestParametersDecorator.php | 7 +- src/Core/Dispatcher/CoreDispatcher.php | 11 +-- src/Core/Dispatcher/DecoratedDispatcher.php | 11 +-- src/Core/Dispatcher/DispatchError.php | 11 +-- src/Core/ErrorHandler/PsrLogErrorHandler.php | 7 +- src/Core/Exception/ExceptionExtra.php | 2 +- .../Runtime/EarlyReturnResponseException.php | 8 +- .../Runtime/InvalidArgumentTypeException.php | 12 +-- .../Exception/Runtime/SnapshotException.php | 13 ++-- src/Core/Handler/ServiceCallback.php | 11 +-- src/Core/Handler/ServiceHandler.php | 7 +- .../Mapping/Parameter/DateTimeTypeMapper.php | 3 +- src/Core/Mapping/Request/BasicEntity.php | 9 ++- src/Core/Mapping/RequestEntityMapping.php | 1 + src/Core/Mapping/RequestParameterMapping.php | 1 + src/Core/Mapping/Response/BasicEntity.php | 2 +- src/Core/Mapping/TReflectionProperties.php | 1 + src/Core/Mapping/Validator/BasicValidator.php | 8 +- .../Mapping/Validator/SymfonyValidator.php | 10 +-- src/Core/Router/SimpleRouter.php | 17 ++--- .../Schema/Builder/Controller/Controller.php | 7 +- src/Core/Schema/Builder/Controller/Method.php | 7 +- src/Core/Schema/Endpoint.php | 8 +- src/Core/Schema/EndpointHandler.php | 12 +-- src/Core/Schema/EndpointNegotiation.php | 7 +- src/Core/Schema/EndpointParameter.php | 33 +++----- src/Core/Schema/EndpointResponse.php | 11 +-- .../Schema/Hierarchy/ControllerMethodPair.php | 11 +-- .../Schema/Hierarchy/HierarchicalNode.php | 8 +- .../Schema/Hierarchy/HierarchyBuilder.php | 10 +-- src/Core/Schema/SchemaInspector.php | 10 +-- .../Schema/Serialization/ArrayHydrator.php | 1 + ...aySerializator.php => ArraySerializer.php} | 5 +- .../{ISerializator.php => ISerializer.php} | 2 +- .../Validation/ControllerPathValidation.php | 4 +- .../Schema/Validation/GroupPathValidation.php | 5 +- .../Validation/NegotiationValidation.php | 3 + src/Core/Schema/Validation/PathValidation.php | 6 +- .../Validation/RequestParameterValidation.php | 8 +- .../Transformer/DebugDataTransformer.php | 11 +-- .../Transformer/DebugTransformer.php | 11 +-- src/Debug/Tracy/Panel/ApiPanel.php | 7 +- src/Middlewares/ApiMiddleware.php | 15 ++-- .../Decorator/ResponseEntityDecorator.php | 7 +- src/Negotiation/DefaultNegotiator.php | 11 +-- src/Negotiation/FallbackNegotiator.php | 7 +- src/Negotiation/Http/AbstractEntity.php | 7 +- src/Negotiation/Http/ArrayEntity.php | 2 +- src/Negotiation/Http/MappingEntity.php | 8 +- src/Negotiation/SuffixNegotiator.php | 7 +- .../Transformer/AbstractTransformer.php | 1 + .../Transformer/CsvTransformer.php | 2 +- .../Transformer/JsonTransformer.php | 1 + .../Transformer/JsonUnifyTransformer.php | 1 + .../Transformer/RendererTransformer.php | 7 +- src/OpenApi/DI/OpenApiPlugin.php | 3 + src/OpenApi/SchemaBuilder.php | 1 + .../SchemaDefinition/ArrayDefinition.php | 8 +- .../SchemaDefinition/CoreDefinition.php | 23 +++--- .../SchemaDefinition/Entity/EntityAdapter.php | 8 +- .../SchemaDefinition/JsonDefinition.php | 9 ++- .../SchemaDefinition/NeonDefinition.php | 9 ++- .../SchemaDefinition/YamlDefinition.php | 8 +- src/OpenApi/SchemaType/BaseSchemaType.php | 75 ++++++++----------- src/Presenter/ApiPresenter.php | 11 +-- .../Serialization/ArraySerializator.phpt | 10 +-- .../Schema/Validation/PathValidation.phpt | 3 +- .../Transformer/DebugDataTransformerTest.php | 2 +- .../Controllers/AttributeMultiController.php | 2 +- 97 files changed, 365 insertions(+), 468 deletions(-) rename src/Core/Schema/Serialization/{ArraySerializator.php => ArraySerializer.php} (97%) rename src/Core/Schema/Serialization/{ISerializator.php => ISerializer.php} (88%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7f75a289..0bdeea0b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,15 +35,9 @@ jobs: with: php: "8.2" - test81: - name: "Nette Tester" - uses: contributte/.github/.github/workflows/nette-tester.yml@master - with: - php: "8.1" - testlower: name: "Nette Tester" uses: contributte/.github/.github/workflows/nette-tester.yml@master with: - php: "8.1" + php: "8.2" composer: "composer update --no-interaction --no-progress --prefer-dist --prefer-stable --prefer-lowest" diff --git a/composer.json b/composer.json index 0eac1cf0..96bb3da6 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "ext-json": "*", "nette/di": "^3.1.8", "contributte/psr7-http-message": "^0.9.0 || ^0.10.0", @@ -33,16 +33,16 @@ "contributte/qa": "^0.4", "contributte/dev": "^0.4", "contributte/tester": "^0.3", - "contributte/phpstan": "^0.1", + "contributte/phpstan": "^0.2", "mockery/mockery": "^1.6.6", "nette/application": "^3.1.4", "nette/di": "^3.1.8", "nette/http": "^3.2.3", "psr/log": "^2.0.0 || ^3.0.0", - "symfony/console": "^6.4.0 || ^7.0.0", - "symfony/translation": "^6.4.0 | ^7.0.0", - "symfony/validator": "^6.4.0 || ^7.0.0", - "symfony/yaml": "^6.4.0 || ^7.0.0", + "symfony/console": "^6.4.0 || ^7.0.0 || ^8.0.0", + "symfony/translation": "^6.4.0 | ^7.0.0 || ^8.0.0", + "symfony/validator": "^6.4.0 || ^7.0.0 || ^8.0.0", + "symfony/yaml": "^6.4.0 || ^7.0.0 || ^8.0.0", "tracy/tracy": "^2.10.5" }, "provide": { diff --git a/phpstan.neon b/phpstan.neon index de32583f..ee8ef80c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -50,3 +50,7 @@ parameters: # 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/ruleset.xml b/ruleset.xml index ab87710d..784e9384 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -3,7 +3,7 @@ Contributte - + @@ -26,7 +26,7 @@ - src/Core/Schema/Serialization/ArraySerializator.php + src/Core/Schema/Serialization/ArraySerializer.php tests/Cases/OpenApi/Schema/ExamplesTest.php diff --git a/src/Console/Command/RouteDumpCommand.php b/src/Console/Command/RouteDumpCommand.php index bd2a0171..7a1bf0e4 100644 --- a/src/Console/Command/RouteDumpCommand.php +++ b/src/Console/Command/RouteDumpCommand.php @@ -21,13 +21,11 @@ class RouteDumpCommand extends Command private const TABLE_HEADER = ['Method', 'Path', 'Handler', ' ', 'Parameters']; - private Schema $schema; - - public function __construct(Schema $schema) + public function __construct( + private readonly Schema $schema, + ) { parent::__construct(); - - $this->schema = $schema; } protected function configure(): void @@ -59,6 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** @var Endpoint[][] $endpointsByHandler */ $endpointsByHandler = []; + foreach ($endpoints as $endpoint) { $endpointsByHandler[$endpoint->getHandler()->getClass()][] = $endpoint; } @@ -115,6 +114,7 @@ private function formatParameters(array $parameters): string foreach ($paramsByIn as $in => $params) { $result .= sprintf('%s', $in) . ': ' . implode(', ', $params); + if ($params !== end($paramsByIn)) { $result .= ' | '; } diff --git a/src/Core/Annotation/Controller/Id.php b/src/Core/Annotation/Controller/Id.php index af6d43c1..3e9dde80 100644 --- a/src/Core/Annotation/Controller/Id.php +++ b/src/Core/Annotation/Controller/Id.php @@ -16,15 +16,13 @@ class Id { - private string $name; - - public function __construct(string $name) + public function __construct( + private readonly string $name, + ) { if ($name === '') { throw new AnnotationException('Empty @Id given'); } - - $this->name = $name; } public function getName(): string diff --git a/src/Core/Annotation/Controller/Method.php b/src/Core/Annotation/Controller/Method.php index 79fa5267..5bb011b0 100644 --- a/src/Core/Annotation/Controller/Method.php +++ b/src/Core/Annotation/Controller/Method.php @@ -17,7 +17,7 @@ class Method { /** @var string[] */ - private array $methods = []; + private readonly array $methods; /** * @param string[]|string $methods @@ -29,7 +29,7 @@ public function __construct(array|string $methods) } // Wrap single given method into array - if (! is_array($methods)) { + if (!is_array($methods)) { $methods = [$methods]; } diff --git a/src/Core/Annotation/Controller/Negotiation.php b/src/Core/Annotation/Controller/Negotiation.php index 0a73ac4b..ddf9b4be 100644 --- a/src/Core/Annotation/Controller/Negotiation.php +++ b/src/Core/Annotation/Controller/Negotiation.php @@ -15,17 +15,12 @@ class Negotiation { - private string $suffix; - - private bool $default; - - private ?string $renderer; - - public function __construct(string $suffix, bool $default = false, ?string $renderer = null) + public function __construct( + private readonly string $suffix, + private readonly bool $default = false, + private readonly ?string $renderer = null, + ) { - $this->suffix = $suffix; - $this->default = $default; - $this->renderer = $renderer; } public function getSuffix(): string diff --git a/src/Core/Annotation/Controller/Negotiations.php b/src/Core/Annotation/Controller/Negotiations.php index 2a29746e..c0fc450c 100644 --- a/src/Core/Annotation/Controller/Negotiations.php +++ b/src/Core/Annotation/Controller/Negotiations.php @@ -15,7 +15,7 @@ class Negotiations { /** @var Negotiation[] */ - private array $negotiations = []; + private readonly array $negotiations; /** * @param Negotiation[]|Negotiation $negotiations diff --git a/src/Core/Annotation/Controller/OpenApi.php b/src/Core/Annotation/Controller/OpenApi.php index a7f0134a..66b83549 100644 --- a/src/Core/Annotation/Controller/OpenApi.php +++ b/src/Core/Annotation/Controller/OpenApi.php @@ -15,7 +15,7 @@ class OpenApi { - private string $data; + private readonly string $data; public function __construct(string $data) { diff --git a/src/Core/Annotation/Controller/Path.php b/src/Core/Annotation/Controller/Path.php index 537ba9fa..4367db46 100644 --- a/src/Core/Annotation/Controller/Path.php +++ b/src/Core/Annotation/Controller/Path.php @@ -16,15 +16,13 @@ class Path { - private string $path; - - public function __construct(string $path) + public function __construct( + private readonly string $path, + ) { if ($path === '') { throw new AnnotationException('Empty @Path given'); } - - $this->path = $path; } public function getPath(): string diff --git a/src/Core/Annotation/Controller/RequestBody.php b/src/Core/Annotation/Controller/RequestBody.php index d3a83577..5e4d381f 100644 --- a/src/Core/Annotation/Controller/RequestBody.php +++ b/src/Core/Annotation/Controller/RequestBody.php @@ -15,20 +15,13 @@ class RequestBody { - private ?string $description; - - private ?string $entity; - - private bool $required; - - private bool $validation; - - public function __construct(?string $description = null, ?string $entity = null, bool $required = false, bool $validation = true) + public function __construct( + private readonly ?string $description = null, + private readonly ?string $entity = null, + private readonly bool $required = false, + private readonly bool $validation = true, + ) { - $this->description = $description; - $this->entity = $entity; - $this->required = $required; - $this->validation = $validation; } public function getEntity(): ?string diff --git a/src/Core/Annotation/Controller/RequestParameter.php b/src/Core/Annotation/Controller/RequestParameter.php index 9fcc11cb..1d76cac5 100644 --- a/src/Core/Annotation/Controller/RequestParameter.php +++ b/src/Core/Annotation/Controller/RequestParameter.php @@ -17,35 +17,18 @@ class RequestParameter { - private string $name; - - private string $type; - - private ?string $description; - - private string $in; - - private bool $required; - - private bool $deprecated; - - private bool $allowEmpty; - - /** @var list|null */ - private ?array $enum; - /** * @param list|null $enum */ public function __construct( - string $name, - string $type, - string $in = EndpointParameter::IN_PATH, - bool $required = true, - bool $allowEmpty = false, - bool $deprecated = false, - ?string $description = null, - ?array $enum = null + private readonly string $name, + private readonly string $type, + private readonly string $in = EndpointParameter::IN_PATH, + private readonly bool $required = true, + private readonly bool $allowEmpty = false, + private readonly bool $deprecated = false, + private readonly ?string $description = null, + private readonly ?array $enum = null ) { if ($name === '') { @@ -59,15 +42,6 @@ public function __construct( if (!in_array($in, EndpointParameter::IN, true)) { throw new AnnotationException('Invalid @RequestParameter in given'); } - - $this->name = $name; - $this->type = $type; - $this->required = $required; - $this->allowEmpty = $allowEmpty; - $this->deprecated = $deprecated; - $this->description = $description; - $this->in = $in; - $this->enum = $enum; } public function getName(): string diff --git a/src/Core/Annotation/Controller/RequestParameters.php b/src/Core/Annotation/Controller/RequestParameters.php index b055f2d4..e319c9bd 100644 --- a/src/Core/Annotation/Controller/RequestParameters.php +++ b/src/Core/Annotation/Controller/RequestParameters.php @@ -15,7 +15,7 @@ class RequestParameters { /** @var RequestParameter[] */ - private array $parameters = []; + private readonly array $parameters; /** * @param RequestParameter[]|RequestParameter $parameters diff --git a/src/Core/Annotation/Controller/Response.php b/src/Core/Annotation/Controller/Response.php index 231eb95a..433574ff 100644 --- a/src/Core/Annotation/Controller/Response.php +++ b/src/Core/Annotation/Controller/Response.php @@ -16,21 +16,15 @@ class Response { - private string $code; - - private string $description; - - private ?string $entity; - - public function __construct(string $description, string $code = 'default', ?string $entity = null) + public function __construct( + private readonly string $description, + private readonly string $code = 'default', + private readonly ?string $entity = null, + ) { if (empty($description)) { throw new AnnotationException('Empty @Response description given'); } - - $this->code = $code; - $this->entity = $entity; - $this->description = $description; } public function getDescription(): string diff --git a/src/Core/Annotation/Controller/Responses.php b/src/Core/Annotation/Controller/Responses.php index adc5e973..9b953bf5 100644 --- a/src/Core/Annotation/Controller/Responses.php +++ b/src/Core/Annotation/Controller/Responses.php @@ -15,7 +15,7 @@ class Responses { /** @var Response[] */ - private array $responses = []; + private readonly array $responses; /** * @param Response[]|Response $responses diff --git a/src/Core/Annotation/Controller/Tag.php b/src/Core/Annotation/Controller/Tag.php index a2c2326f..0727ca5f 100644 --- a/src/Core/Annotation/Controller/Tag.php +++ b/src/Core/Annotation/Controller/Tag.php @@ -16,18 +16,14 @@ class Tag { - private string $name; - - private ?string $value; - - public function __construct(string $name, ?string $value = null) + public function __construct( + private readonly string $name, + private readonly ?string $value = null, + ) { if ($name === '') { throw new AnnotationException('Empty @Tag name given'); } - - $this->name = $name; - $this->value = $value; } public function getName(): string diff --git a/src/Core/Application/Application.php b/src/Core/Application/Application.php index 609f2bf9..e6dd13a6 100644 --- a/src/Core/Application/Application.php +++ b/src/Core/Application/Application.php @@ -11,13 +11,12 @@ class Application extends BaseApplication { - private IDispatcher $dispatcher; - - public function __construct(IErrorHandler $errorHandler, IDispatcher $dispatcher) + public function __construct( + IErrorHandler $errorHandler, + private readonly IDispatcher $dispatcher, + ) { parent::__construct($errorHandler); - - $this->dispatcher = $dispatcher; } protected function dispatch(ApiRequest $request): ApiResponse diff --git a/src/Core/Application/BaseApplication.php b/src/Core/Application/BaseApplication.php index 3b463c21..78c8036d 100644 --- a/src/Core/Application/BaseApplication.php +++ b/src/Core/Application/BaseApplication.php @@ -16,11 +16,10 @@ abstract class BaseApplication implements IApplication 'content-type', ]; - private IErrorHandler $errorHandler; - - public function __construct(IErrorHandler $errorHandler) + public function __construct( + private readonly IErrorHandler $errorHandler, + ) { - $this->errorHandler = $errorHandler; } public function run(): void @@ -55,6 +54,7 @@ protected function sendResponse(ApiResponse $response): void foreach ($response->getHeaders() as $name => $values) { $replace = in_array(strtolower($name), self::UNIQUE_HEADERS, true); + foreach ($values as $value) { header(sprintf('%s: %s', $name, $value), $replace); } diff --git a/src/Core/DI/Loader/AbstractContainerLoader.php b/src/Core/DI/Loader/AbstractContainerLoader.php index af965512..85a6b82f 100644 --- a/src/Core/DI/Loader/AbstractContainerLoader.php +++ b/src/Core/DI/Loader/AbstractContainerLoader.php @@ -9,11 +9,10 @@ abstract class AbstractContainerLoader implements ILoader { - private ContainerBuilder $builder; - - public function __construct(ContainerBuilder $builder) + public function __construct( + private readonly ContainerBuilder $builder, + ) { - $this->builder = $builder; } /** diff --git a/src/Core/DI/Loader/DoctrineAnnotationLoader.php b/src/Core/DI/Loader/DoctrineAnnotationLoader.php index 41b30e37..99d2c178 100644 --- a/src/Core/DI/Loader/DoctrineAnnotationLoader.php +++ b/src/Core/DI/Loader/DoctrineAnnotationLoader.php @@ -93,10 +93,13 @@ protected function analyseClass(string $class): ReflectionClass ]; // Get all parents - /** @var class-string[] $parents */ $parents = class_parents($class); $reflections = []; + if ($parents === false) { + $parents = []; + } + // Iterate over all parents and analyse them foreach ($parents as $parentClass) { // Stop multiple analysing diff --git a/src/Core/DI/Loader/NeonLoader.php b/src/Core/DI/Loader/NeonLoader.php index 900a4930..b721b2b4 100644 --- a/src/Core/DI/Loader/NeonLoader.php +++ b/src/Core/DI/Loader/NeonLoader.php @@ -11,15 +11,13 @@ class NeonLoader implements ILoader { - /** @var mixed[] */ - private array $schema; - /** * @param mixed[] $schema */ - public function __construct(array $schema) + public function __construct( + private readonly array $schema, + ) { - $this->schema = $schema; } public function load(SchemaBuilder $builder): SchemaBuilder diff --git a/src/Core/DI/LoaderFactory/DualReaderFactory.php b/src/Core/DI/LoaderFactory/DualReaderFactory.php index a3e7e0ec..5752c2f9 100644 --- a/src/Core/DI/LoaderFactory/DualReaderFactory.php +++ b/src/Core/DI/LoaderFactory/DualReaderFactory.php @@ -17,6 +17,7 @@ class DualReaderFactory public function create(): Reader { $annotationReader = new AnnotationReader(); + if (method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { AnnotationRegistry::registerUniqueLoader('class_exists'); } diff --git a/src/Core/DI/Plugin/CoreDecoratorPlugin.php b/src/Core/DI/Plugin/CoreDecoratorPlugin.php index a84429b8..b4299463 100644 --- a/src/Core/DI/Plugin/CoreDecoratorPlugin.php +++ b/src/Core/DI/Plugin/CoreDecoratorPlugin.php @@ -50,18 +50,21 @@ protected function compileDecorators(): void $requestDecoratorDefinitions = $builder->findByType(IRequestDecorator::class); $requestDecoratorDefinitions = Helpers::sortByPriorityInTag(ApiExtension::CORE_DECORATOR_TAG, $requestDecoratorDefinitions); + foreach ($requestDecoratorDefinitions as $decoratorDefinition) { $managerDefinition->addSetup('addRequestDecorator', [$decoratorDefinition]); } $responseDecoratorDefinitions = $builder->findByType(IResponseDecorator::class); $responseDecoratorDefinitions = Helpers::sortByPriorityInTag(ApiExtension::CORE_DECORATOR_TAG, $responseDecoratorDefinitions); + foreach ($responseDecoratorDefinitions as $decoratorDefinition) { $managerDefinition->addSetup('addResponseDecorator', [$decoratorDefinition]); } $errorDecoratorDefinitions = $builder->findByType(IErrorDecorator::class); $errorDecoratorDefinitions = Helpers::sortByPriorityInTag(ApiExtension::CORE_DECORATOR_TAG, $errorDecoratorDefinitions); + foreach ($errorDecoratorDefinitions as $decoratorDefinition) { $managerDefinition->addSetup('addErrorDecorator', [$decoratorDefinition]); } diff --git a/src/Core/DI/Plugin/CoreSchemaPlugin.php b/src/Core/DI/Plugin/CoreSchemaPlugin.php index 3fb915c7..8740f151 100644 --- a/src/Core/DI/Plugin/CoreSchemaPlugin.php +++ b/src/Core/DI/Plugin/CoreSchemaPlugin.php @@ -7,7 +7,7 @@ use Apitte\Core\DI\Loader\NeonLoader; use Apitte\Core\Schema\SchemaBuilder; use Apitte\Core\Schema\Serialization\ArrayHydrator; -use Apitte\Core\Schema\Serialization\ArraySerializator; +use Apitte\Core\Schema\Serialization\ArraySerializer; use Apitte\Core\Schema\Serialization\IDecorator; use Apitte\Core\Schema\Validation\ControllerPathValidation; use Apitte\Core\Schema\Validation\ControllerValidation; @@ -105,7 +105,7 @@ protected function compileSchema(): array } // Convert schema to array (for DI) - $generator = new ArraySerializator(); + $generator = new ArraySerializer(); return $generator->serialize($builder); } @@ -115,7 +115,6 @@ protected function loadSchema(SchemaBuilder $builder): SchemaBuilder $loaders = $this->config->loaders; if ($loaders->annotations->enable) { - if (!class_exists($loaders->annotations->loader)) { throw new \RuntimeException(sprintf('Annotation loader class %s does not exist', $loaders->annotations->loader)); } @@ -134,6 +133,7 @@ protected function loadSchema(SchemaBuilder $builder): SchemaBuilder // Load schema from files $adapter = new NeonAdapter(); $schema = []; + foreach ($files as $file) { $schema = Arrays::mergeTree($schema, $adapter->load($file)); } @@ -147,18 +147,19 @@ protected function loadSchema(SchemaBuilder $builder): SchemaBuilder protected function validateSchema(SchemaBuilder $builder): SchemaBuilder { + /** @var class-string[] $validations */ $validations = $this->config->validations; $validator = new SchemaBuilderValidator(); // Add all validators at compile-time - /** @var class-string $validation */ foreach ($validations as $validation) { $validator->add(new $validation()); } /** @var ?CoreMappingPlugin $coreMappingPlugin */ $coreMappingPlugin = $this->compiler->getPlugin(CoreMappingPlugin::getName()); + if ($coreMappingPlugin !== null) { $validator->add(new RequestParameterValidation($coreMappingPlugin->getAllowedTypes())); } diff --git a/src/Core/DI/Plugin/Plugin.php b/src/Core/DI/Plugin/Plugin.php index d80ad073..c38cf07d 100644 --- a/src/Core/DI/Plugin/Plugin.php +++ b/src/Core/DI/Plugin/Plugin.php @@ -15,14 +15,13 @@ abstract class Plugin { - protected PluginCompiler $compiler; - /** @var stdClass|mixed[] */ protected stdClass|array $config; - public function __construct(PluginCompiler $compiler) + public function __construct( + protected PluginCompiler $compiler, + ) { - $this->compiler = $compiler; } abstract public static function getName(): string; @@ -67,6 +66,7 @@ protected function setupConfig(Schema $schema, array $config, string $name): voi $processor->onNewContext[] = static function (Context $context) use ($name): void { $context->path = [$name]; }; + try { $this->config = $processor->process($schema, $config); } catch (ValidationException $exception) { diff --git a/src/Core/DI/Plugin/PluginCompiler.php b/src/Core/DI/Plugin/PluginCompiler.php index 7e7fe476..d2c822a8 100644 --- a/src/Core/DI/Plugin/PluginCompiler.php +++ b/src/Core/DI/Plugin/PluginCompiler.php @@ -7,14 +7,11 @@ class PluginCompiler { - protected PluginManager $manager; - - protected ApiExtension $extension; - - public function __construct(PluginManager $manager, ApiExtension $extension) + public function __construct( + protected PluginManager $manager, + protected ApiExtension $extension, + ) { - $this->manager = $manager; - $this->extension = $extension; } public function getExtension(): ApiExtension diff --git a/src/Core/Decorator/RequestEntityDecorator.php b/src/Core/Decorator/RequestEntityDecorator.php index f50a3e24..bc8818e7 100644 --- a/src/Core/Decorator/RequestEntityDecorator.php +++ b/src/Core/Decorator/RequestEntityDecorator.php @@ -9,11 +9,10 @@ class RequestEntityDecorator implements IRequestDecorator { - protected RequestEntityMapping $mapping; - - public function __construct(RequestEntityMapping $mapping) + public function __construct( + protected RequestEntityMapping $mapping, + ) { - $this->mapping = $mapping; } public function decorateRequest(ApiRequest $request, ApiResponse $response): ApiRequest diff --git a/src/Core/Decorator/RequestParametersDecorator.php b/src/Core/Decorator/RequestParametersDecorator.php index fa5ba48f..2dad2ffb 100644 --- a/src/Core/Decorator/RequestParametersDecorator.php +++ b/src/Core/Decorator/RequestParametersDecorator.php @@ -9,11 +9,10 @@ class RequestParametersDecorator implements IRequestDecorator { - protected RequestParameterMapping $mapping; - - public function __construct(RequestParameterMapping $mapping) + public function __construct( + protected RequestParameterMapping $mapping, + ) { - $this->mapping = $mapping; } public function decorateRequest(ApiRequest $request, ApiResponse $response): ApiRequest diff --git a/src/Core/Dispatcher/CoreDispatcher.php b/src/Core/Dispatcher/CoreDispatcher.php index 2776708b..725b7a29 100644 --- a/src/Core/Dispatcher/CoreDispatcher.php +++ b/src/Core/Dispatcher/CoreDispatcher.php @@ -13,14 +13,11 @@ class CoreDispatcher implements IDispatcher { - protected IRouter $router; - - protected IHandler $handler; - - public function __construct(IRouter $router, IHandler $handler) + public function __construct( + protected IRouter $router, + protected IHandler $handler, + ) { - $this->router = $router; - $this->handler = $handler; } public function dispatch(ApiRequest $request, ApiResponse $response): ApiResponse diff --git a/src/Core/Dispatcher/DecoratedDispatcher.php b/src/Core/Dispatcher/DecoratedDispatcher.php index 41df0c49..e441ef0a 100644 --- a/src/Core/Dispatcher/DecoratedDispatcher.php +++ b/src/Core/Dispatcher/DecoratedDispatcher.php @@ -23,13 +23,13 @@ class DecoratedDispatcher extends CoreDispatcher { - protected DecoratorManager $decoratorManager; - - public function __construct(IRouter $router, IHandler $handler, DecoratorManager $decoratorManager) + public function __construct( + IRouter $router, + IHandler $handler, + protected DecoratorManager $decoratorManager, + ) { parent::__construct($router, $handler); - - $this->decoratorManager = $decoratorManager; } public function dispatch(ApiRequest $request, ApiResponse $response): ApiResponse @@ -67,6 +67,7 @@ protected function handle(ApiRequest $request, ApiResponse $response): ApiRespon { // Pass endpoint to response $endpoint = $request->getAttribute(RequestAttributes::ATTR_ENDPOINT, null); + if ($endpoint !== null) { $response = $response->withEndpoint($endpoint); } diff --git a/src/Core/Dispatcher/DispatchError.php b/src/Core/Dispatcher/DispatchError.php index cabe94dd..c7921dde 100644 --- a/src/Core/Dispatcher/DispatchError.php +++ b/src/Core/Dispatcher/DispatchError.php @@ -8,14 +8,11 @@ class DispatchError { - protected Throwable $error; - - protected ApiRequest $request; - - public function __construct(Throwable $error, ApiRequest $request) + public function __construct( + protected Throwable $error, + protected ApiRequest $request, + ) { - $this->error = $error; - $this->request = $request; } public function getError(): Throwable diff --git a/src/Core/ErrorHandler/PsrLogErrorHandler.php b/src/Core/ErrorHandler/PsrLogErrorHandler.php index 51c63a4e..447785b9 100644 --- a/src/Core/ErrorHandler/PsrLogErrorHandler.php +++ b/src/Core/ErrorHandler/PsrLogErrorHandler.php @@ -13,11 +13,10 @@ class PsrLogErrorHandler extends SimpleErrorHandler { - private LoggerInterface $logger; - - public function __construct(LoggerInterface $logger) + public function __construct( + private readonly LoggerInterface $logger, + ) { - $this->logger = $logger; } public function handle(DispatchError $dispatchError): ApiResponse diff --git a/src/Core/Exception/ExceptionExtra.php b/src/Core/Exception/ExceptionExtra.php index a0153cd8..01e093a6 100644 --- a/src/Core/Exception/ExceptionExtra.php +++ b/src/Core/Exception/ExceptionExtra.php @@ -38,7 +38,7 @@ public function withCode(int $code): static */ public function withMessage(string|array $message): static { - $this->message = $message; + $this->message = is_array($message) ? implode(';', $message) : $message; return $this; } diff --git a/src/Core/Exception/Runtime/EarlyReturnResponseException.php b/src/Core/Exception/Runtime/EarlyReturnResponseException.php index cf3733d4..e4caf592 100644 --- a/src/Core/Exception/Runtime/EarlyReturnResponseException.php +++ b/src/Core/Exception/Runtime/EarlyReturnResponseException.php @@ -8,13 +8,11 @@ class EarlyReturnResponseException extends RuntimeException { - protected ApiResponse $response; - - public function __construct(ApiResponse $response) + public function __construct( + protected ApiResponse $response, + ) { parent::__construct(); - - $this->response = $response; } public function getResponse(): ApiResponse diff --git a/src/Core/Exception/Runtime/InvalidArgumentTypeException.php b/src/Core/Exception/Runtime/InvalidArgumentTypeException.php index 733a09f1..d0bb4dc3 100644 --- a/src/Core/Exception/Runtime/InvalidArgumentTypeException.php +++ b/src/Core/Exception/Runtime/InvalidArgumentTypeException.php @@ -13,16 +13,12 @@ class InvalidArgumentTypeException extends RuntimeException public const TYPE_DATETIME = 'datetime'; public const TYPE_ENUM = 'enum'; - private string $type; - - private ?string $description; - - public function __construct(string $type, ?string $description = null) + public function __construct( + private readonly string $type, + private readonly ?string $description = null, + ) { parent::__construct(); - - $this->type = $type; - $this->description = $description; } public function getType(): string diff --git a/src/Core/Exception/Runtime/SnapshotException.php b/src/Core/Exception/Runtime/SnapshotException.php index b09a0453..aa032e62 100644 --- a/src/Core/Exception/Runtime/SnapshotException.php +++ b/src/Core/Exception/Runtime/SnapshotException.php @@ -13,16 +13,13 @@ class SnapshotException extends RuntimeException { - protected ApiRequest $request; - - protected ApiResponse $response; - - public function __construct(Throwable $exception, ApiRequest $request, ApiResponse $response) + public function __construct( + Throwable $exception, + protected ApiRequest $request, + protected ApiResponse $response, + ) { parent::__construct($exception->getMessage(), is_string($exception->getCode()) ? -1 : $exception->getCode(), $exception); - - $this->request = $request; - $this->response = $response; } public function getRequest(): ApiRequest diff --git a/src/Core/Handler/ServiceCallback.php b/src/Core/Handler/ServiceCallback.php index 940cecd6..b262a2fd 100644 --- a/src/Core/Handler/ServiceCallback.php +++ b/src/Core/Handler/ServiceCallback.php @@ -10,14 +10,11 @@ class ServiceCallback { - private IController $service; - - private string $method; - - public function __construct(IController $service, string $method) + public function __construct( + private readonly IController $service, + private readonly string $method, + ) { - $this->service = $service; - $this->method = $method; } public function getService(): IController diff --git a/src/Core/Handler/ServiceHandler.php b/src/Core/Handler/ServiceHandler.php index b1d195de..782478cd 100644 --- a/src/Core/Handler/ServiceHandler.php +++ b/src/Core/Handler/ServiceHandler.php @@ -14,11 +14,10 @@ class ServiceHandler implements IHandler { - protected Container $container; - - public function __construct(Container $container) + public function __construct( + protected Container $container, + ) { - $this->container = $container; } public function handle(ApiRequest $request, ApiResponse $response): mixed diff --git a/src/Core/Mapping/Parameter/DateTimeTypeMapper.php b/src/Core/Mapping/Parameter/DateTimeTypeMapper.php index 89170ab0..031e8fe2 100644 --- a/src/Core/Mapping/Parameter/DateTimeTypeMapper.php +++ b/src/Core/Mapping/Parameter/DateTimeTypeMapper.php @@ -5,6 +5,7 @@ use Apitte\Core\Exception\Runtime\InvalidArgumentTypeException; use DateTimeImmutable; use TypeError; +use ValueError; class DateTimeTypeMapper implements ITypeMapper { @@ -16,7 +17,7 @@ public function normalize(mixed $value, array $options = []): ?DateTimeImmutable { try { $value = DateTimeImmutable::createFromFormat(DATE_ATOM, $value); - } catch (TypeError $e) { + } catch (ValueError | TypeError) { throw new InvalidArgumentTypeException(InvalidArgumentTypeException::TYPE_DATETIME); } diff --git a/src/Core/Mapping/Request/BasicEntity.php b/src/Core/Mapping/Request/BasicEntity.php index b2ba5189..fa5b0811 100644 --- a/src/Core/Mapping/Request/BasicEntity.php +++ b/src/Core/Mapping/Request/BasicEntity.php @@ -47,15 +47,18 @@ public function fromRequest(ApiRequest $request): ?IRequestEntity * @param array $data * @return static */ - public function factory(array $data): self + public function factory(array $data): static { + /** @var static $inst */ $inst = new static(); // Fill properties with real data $properties = $inst->getRequestProperties(); + foreach ($properties as $property) { /** @var TKey $propName */ $propName = $property['name']; + if (!array_key_exists($propName, $data)) { continue; } @@ -70,13 +73,17 @@ public function factory(array $data): self // Fill single property try { $propNameStr = (string) $propName; + if (property_exists($inst, $propNameStr)) { $ref = new \ReflectionProperty($inst, $propNameStr); $wasAccessible = $ref->isPublic(); + if (!$wasAccessible) { $ref->setAccessible(true); } + $ref->setValue($inst, $value); + if (!$wasAccessible) { $ref->setAccessible(false); } diff --git a/src/Core/Mapping/RequestEntityMapping.php b/src/Core/Mapping/RequestEntityMapping.php index 3791143f..123c4e82 100644 --- a/src/Core/Mapping/RequestEntityMapping.php +++ b/src/Core/Mapping/RequestEntityMapping.php @@ -32,6 +32,7 @@ public function map(ApiRequest $request, ApiResponse $response): ApiRequest } $requestBody = $endpoint->getRequestBody(); + // If there's no request mapper, then skip it if ($requestBody === null) { return $request; diff --git a/src/Core/Mapping/RequestParameterMapping.php b/src/Core/Mapping/RequestParameterMapping.php index 340978b3..32418486 100644 --- a/src/Core/Mapping/RequestParameterMapping.php +++ b/src/Core/Mapping/RequestParameterMapping.php @@ -133,6 +133,7 @@ public function map(ApiRequest $request, ApiResponse $response): ApiRequest case $parameter::IN_HEADER: $headerParameterName = strtolower($parameter->getName()); + // Logical check if (!array_key_exists($headerParameterName, $headerParameters)) { if (!$parameter->isRequired()) { diff --git a/src/Core/Mapping/Response/BasicEntity.php b/src/Core/Mapping/Response/BasicEntity.php index 8a116778..1bf9a2ae 100644 --- a/src/Core/Mapping/Response/BasicEntity.php +++ b/src/Core/Mapping/Response/BasicEntity.php @@ -15,7 +15,7 @@ abstract class BasicEntity extends AbstractEntity use TReflectionProperties; /** - * @return array> + * @return array> */ public function getResponseProperties(): array { diff --git a/src/Core/Mapping/TReflectionProperties.php b/src/Core/Mapping/TReflectionProperties.php index 886df0c5..21bc4c45 100644 --- a/src/Core/Mapping/TReflectionProperties.php +++ b/src/Core/Mapping/TReflectionProperties.php @@ -21,6 +21,7 @@ public function getProperties(): array $class = static::class; $defaultProperties = $rf->getDefaultProperties(); + foreach ($rf->getProperties() as $property) { // If property is not from the latest child, then skip it. if ($property->getDeclaringClass()->getName() !== $class) { diff --git a/src/Core/Mapping/Validator/BasicValidator.php b/src/Core/Mapping/Validator/BasicValidator.php index d987695b..11e4d62d 100644 --- a/src/Core/Mapping/Validator/BasicValidator.php +++ b/src/Core/Mapping/Validator/BasicValidator.php @@ -24,11 +24,11 @@ public function validate(object $entity): void $violations = $this->validateProperties($entity); if ($violations !== []) { - $fields = []; - foreach ($violations as $property => $messages) { - $fields[$property] = count($messages) > 1 ? $messages : $messages[0]; - } + $fields = array_map( + static fn ($messages) => count($messages) > 1 ? $messages : $messages[0], + $violations, + ); throw ValidationException::create() ->withFields($fields); diff --git a/src/Core/Mapping/Validator/SymfonyValidator.php b/src/Core/Mapping/Validator/SymfonyValidator.php index 3e4c5c4c..8fbe51b6 100644 --- a/src/Core/Mapping/Validator/SymfonyValidator.php +++ b/src/Core/Mapping/Validator/SymfonyValidator.php @@ -6,15 +6,12 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\Reader; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; -use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Validation; use Symfony\Contracts\Translation\TranslatorInterface; class SymfonyValidator implements IEntityValidator { - private ?Reader $reader; - private ?ConstraintValidatorFactoryInterface $constraintValidatorFactory = null; private ?TranslatorInterface $translator = null; @@ -24,9 +21,10 @@ class SymfonyValidator implements IEntityValidator /** @var list|null */ private ?array $groups = null; - public function __construct(?Reader $reader = null) + public function __construct( + private ?Reader $reader = null, + ) { - $this->reader = $reader; AnnotationReader::addGlobalIgnoredName('mapping'); } @@ -77,11 +75,11 @@ public function validate(object $entity): void $validator = $validatorBuilder->getValidator(); - /** @var ConstraintViolationListInterface $violations */ $violations = $validator->validate($entity, null, $this->groups); if (count($violations) > 0) { $fields = []; + foreach ($violations as $violation) { $fields[$violation->getPropertyPath()][] = $violation->getMessage(); } diff --git a/src/Core/Router/SimpleRouter.php b/src/Core/Router/SimpleRouter.php index e13f5ba0..111c5c6b 100644 --- a/src/Core/Router/SimpleRouter.php +++ b/src/Core/Router/SimpleRouter.php @@ -13,11 +13,10 @@ class SimpleRouter implements IRouter { - private Schema $schema; - - public function __construct(Schema $schema) + public function __construct( + private readonly Schema $schema, + ) { - $this->schema = $schema; } public function match(ApiRequest $request): ?ApiRequest @@ -26,6 +25,7 @@ public function match(ApiRequest $request): ?ApiRequest $exception = null; $matched = null; + // Iterate over all endpoints foreach ($endpoints as $endpoint) { try { @@ -42,10 +42,8 @@ public function match(ApiRequest $request): ?ApiRequest // If matched is not null, returns given ServerRequestInterface // with all parsed arguments and data, // also append given Endpoint - $matched = $matched + return $matched ->withAttribute(RequestAttributes::ATTR_ENDPOINT, $endpoint); - - return $matched; } if ($exception !== null) { @@ -95,17 +93,16 @@ protected function compareUrl(Endpoint $endpoint, ApiRequest $request): ?ApiRequ // Fill query parameters with query params $queryParams = $request->getQueryParams(); + foreach ($endpoint->getParametersByIn(EndpointParameter::IN_QUERY) as $param) { $name = $param->getName(); $parameters[$name] = $queryParams[$name] ?? null; } // Set attributes to request - $request = $request + return $request ->withAttribute(RequestAttributes::ATTR_ROUTER, $match) ->withAttribute(RequestAttributes::ATTR_PARAMETERS, $parameters); - - return $request; } } diff --git a/src/Core/Schema/Builder/Controller/Controller.php b/src/Core/Schema/Builder/Controller/Controller.php index 405b08ed..afaf35b1 100644 --- a/src/Core/Schema/Builder/Controller/Controller.php +++ b/src/Core/Schema/Builder/Controller/Controller.php @@ -5,8 +5,6 @@ class Controller { - private string $class; - /** @var Method[] */ private array $methods = []; @@ -26,9 +24,10 @@ class Controller /** @var mixed[] */ private array $openApi = []; - public function __construct(string $class) + public function __construct( + private readonly string $class, + ) { - $this->class = $class; } public function getClass(): string diff --git a/src/Core/Schema/Builder/Controller/Method.php b/src/Core/Schema/Builder/Controller/Method.php index ded60970..590a62eb 100644 --- a/src/Core/Schema/Builder/Controller/Method.php +++ b/src/Core/Schema/Builder/Controller/Method.php @@ -10,8 +10,6 @@ class Method { - private string $name; - private string $path = ''; private ?string $id = null; @@ -36,9 +34,10 @@ class Method /** @var mixed[] */ private array $openApi = []; - public function __construct(string $name) + public function __construct( + private readonly string $name, + ) { - $this->name = $name; } public function getName(): string diff --git a/src/Core/Schema/Endpoint.php b/src/Core/Schema/Endpoint.php index 70c165e3..bdf1fc5a 100644 --- a/src/Core/Schema/Endpoint.php +++ b/src/Core/Schema/Endpoint.php @@ -38,8 +38,6 @@ class Endpoint private ?string $pattern = null; - private EndpointHandler $handler; - /** @var EndpointParameter[] */ private array $parameters = []; @@ -60,9 +58,10 @@ class Endpoint /** @var mixed[] */ private array $openApi = []; - public function __construct(EndpointHandler $handler) + public function __construct( + private readonly EndpointHandler $handler, + ) { - $this->handler = $handler; } /** @@ -281,6 +280,7 @@ private function generatePattern(): string } $suffixes = []; + foreach ($this->getNegotiations() as $negotiation) { $suffix = $negotiation->getSuffix(); diff --git a/src/Core/Schema/EndpointHandler.php b/src/Core/Schema/EndpointHandler.php index 8f159d4d..5857b3d8 100644 --- a/src/Core/Schema/EndpointHandler.php +++ b/src/Core/Schema/EndpointHandler.php @@ -5,18 +5,14 @@ class EndpointHandler { - /** @var class-string */ - private string $class; - - private string $method; - /** * @param class-string $class */ - public function __construct(string $class, string $method) + public function __construct( + private readonly string $class, + private readonly string $method, + ) { - $this->class = $class; - $this->method = $method; } /** diff --git a/src/Core/Schema/EndpointNegotiation.php b/src/Core/Schema/EndpointNegotiation.php index e6c9566b..22e9ab65 100644 --- a/src/Core/Schema/EndpointNegotiation.php +++ b/src/Core/Schema/EndpointNegotiation.php @@ -5,15 +5,14 @@ class EndpointNegotiation { - private string $suffix; - private bool $default = false; private ?string $renderer = null; - public function __construct(string $suffix) + public function __construct( + private readonly string $suffix, + ) { - $this->suffix = $suffix; } public function getSuffix(): string diff --git a/src/Core/Schema/EndpointParameter.php b/src/Core/Schema/EndpointParameter.php index eccf2fc4..85ce4d32 100644 --- a/src/Core/Schema/EndpointParameter.php +++ b/src/Core/Schema/EndpointParameter.php @@ -33,10 +33,6 @@ class EndpointParameter self::IN_PATH, ]; - private string $name; - - private string $type; - private ?string $description = null; private string $in = self::IN_PATH; @@ -50,10 +46,11 @@ class EndpointParameter /** @var list|null */ private ?array $enum = null; - public function __construct(string $name, string $type = self::TYPE_STRING) + public function __construct( + private readonly string $name, + private readonly string $type = self::TYPE_STRING, + ) { - $this->name = $name; - $this->type = $type; } public function getName(): string @@ -68,21 +65,13 @@ public function getType(): string public function getSchemaType(): string { - switch ($this->type) { - case self::TYPE_STRING: - case self::TYPE_FLOAT: - case self::TYPE_DATETIME: - return $this->type; - case self::TYPE_BOOLEAN: - return 'boolean'; - case self::TYPE_INTEGER: - return 'integer'; - case self::TYPE_ENUM: - return 'string'; - default: - // custom type - return 'string'; - } + return match ($this->type) { + self::TYPE_STRING, self::TYPE_FLOAT, self::TYPE_DATETIME => $this->type, + self::TYPE_BOOLEAN => 'boolean', + self::TYPE_INTEGER => 'integer', + self::TYPE_ENUM => 'string', + default => 'string', + }; } public function getDescription(): ?string diff --git a/src/Core/Schema/EndpointResponse.php b/src/Core/Schema/EndpointResponse.php index 96504916..646f5069 100644 --- a/src/Core/Schema/EndpointResponse.php +++ b/src/Core/Schema/EndpointResponse.php @@ -5,16 +5,13 @@ class EndpointResponse { - private string $code; - - private string $description; - private ?string $entity = null; - public function __construct(string $code, string $description) + public function __construct( + private readonly string $code, + private readonly string $description, + ) { - $this->code = $code; - $this->description = $description; } public function setEntity(?string $entity): void diff --git a/src/Core/Schema/Hierarchy/ControllerMethodPair.php b/src/Core/Schema/Hierarchy/ControllerMethodPair.php index 802f4e34..9f4fd57f 100644 --- a/src/Core/Schema/Hierarchy/ControllerMethodPair.php +++ b/src/Core/Schema/Hierarchy/ControllerMethodPair.php @@ -8,14 +8,11 @@ class ControllerMethodPair { - private Controller $controller; - - private Method $method; - - public function __construct(Controller $controller, Method $method) + public function __construct( + private readonly Controller $controller, + private readonly Method $method, + ) { - $this->controller = $controller; - $this->method = $method; } public function getController(): Controller diff --git a/src/Core/Schema/Hierarchy/HierarchicalNode.php b/src/Core/Schema/Hierarchy/HierarchicalNode.php index 74f39826..1fe0fb8b 100644 --- a/src/Core/Schema/Hierarchy/HierarchicalNode.php +++ b/src/Core/Schema/Hierarchy/HierarchicalNode.php @@ -5,17 +5,16 @@ class HierarchicalNode { - private string $path; - /** @var HierarchicalNode[] */ private array $nodes = []; /** @var ControllerMethodPair[] */ private array $endpoints = []; - public function __construct(string $path) + public function __construct( + private readonly string $path, + ) { - $this->path = $path; } public function getPath(): string @@ -64,6 +63,7 @@ public function getSortedNodes(): array // Divide static and variable nodes foreach ($this->nodes as $node) { $path = $node->getPath(); + if (str_contains($path, '{') && str_contains($path, '}')) { $variableNodes[] = $node; } else { diff --git a/src/Core/Schema/Hierarchy/HierarchyBuilder.php b/src/Core/Schema/Hierarchy/HierarchyBuilder.php index eab351cb..df207d51 100644 --- a/src/Core/Schema/Hierarchy/HierarchyBuilder.php +++ b/src/Core/Schema/Hierarchy/HierarchyBuilder.php @@ -7,18 +7,16 @@ class HierarchyBuilder { - /** @var Controller[] */ - private array $controllers; - /** @var HierarchicalNode[] */ private array $nodes = []; /** * @param Controller[] $controllers */ - public function __construct(array $controllers) + public function __construct( + private readonly array $controllers, + ) { - $this->controllers = $controllers; } public function getHierarchy(): HierarchicalNode @@ -33,6 +31,7 @@ public function getHierarchy(): HierarchicalNode foreach ($controller->getMethods() as $method) { $methodPathParts = $this->splitPathParts($method->getPath()); $allPathParts = array_merge($controllerPathParts, $methodPathParts); + if ($allPathParts === []) { // Full path to endpoint is just /, it's a root node $rootNode->addEndpoint(new ControllerMethodPair($controller, $method)); @@ -40,6 +39,7 @@ public function getHierarchy(): HierarchicalNode $lastPathPartKey = array_keys($allPathParts)[count($allPathParts) - 1]; // array_key_last for php < 7.3.0 $previousNode = $rootNode; + foreach ($allPathParts as $key => $part) { $node = $previousNode->addNode($part); diff --git a/src/Core/Schema/SchemaInspector.php b/src/Core/Schema/SchemaInspector.php index 214e9602..ed837189 100644 --- a/src/Core/Schema/SchemaInspector.php +++ b/src/Core/Schema/SchemaInspector.php @@ -5,14 +5,13 @@ class SchemaInspector { - private Schema $schema; - /** @var Endpoint[][] */ private array $cache = []; - public function __construct(Schema $schema) + public function __construct( + private readonly Schema $schema, + ) { - $this->schema = $schema; } /** @@ -25,8 +24,9 @@ public function getEndpointsByTag(string $name, ?string $value = null): array if (!isset($this->cache[$key])) { $items = []; + foreach ($endpoints as $endpoint) { - // Skip if endpoint does not have a tag + // Skip if the endpoint does not have a tag if (!$endpoint->hasTag($name)) { continue; } diff --git a/src/Core/Schema/Serialization/ArrayHydrator.php b/src/Core/Schema/Serialization/ArrayHydrator.php index 786a5ceb..050b4ecd 100644 --- a/src/Core/Schema/Serialization/ArrayHydrator.php +++ b/src/Core/Schema/Serialization/ArrayHydrator.php @@ -98,6 +98,7 @@ private function hydrateEndpoint(array $data): Endpoint $res['code'], $res['description'] ); + if (isset($res['entity'])) { $response->setEntity($res['entity']); } diff --git a/src/Core/Schema/Serialization/ArraySerializator.php b/src/Core/Schema/Serialization/ArraySerializer.php similarity index 97% rename from src/Core/Schema/Serialization/ArraySerializator.php rename to src/Core/Schema/Serialization/ArraySerializer.php index af983f0b..2dd567b9 100644 --- a/src/Core/Schema/Serialization/ArraySerializator.php +++ b/src/Core/Schema/Serialization/ArraySerializer.php @@ -11,7 +11,7 @@ use Apitte\Core\Utils\Helpers; use Apitte\Core\Utils\Regex; -class ArraySerializator implements ISerializator +class ArraySerializer implements ISerializer { /** @@ -115,10 +115,8 @@ private function serializePattern(array &$endpoint, Controller $controller, Meth $mask = $endpoint['mask']; $maskParameters = []; - /** @var EndpointParameter[] $pathParameters */ $pathParameters = array_filter($method->getParameters(), static fn (EndpointParameter $parameter): bool => $parameter->getIn() === EndpointParameter::IN_PATH); - /** @var EndpointParameter[] $notPathParameters */ $notPathParameters = array_filter($method->getParameters(), static fn (EndpointParameter $parameter): bool => $parameter->getIn() !== EndpointParameter::IN_PATH); // Collect variable parameters from URL @@ -251,6 +249,7 @@ private function serializeEndpointResponses(array &$endpoint, Method $method): v 'code' => $response->getCode(), 'description' => $response->getDescription(), ]; + if ($response->getEntity() !== null) { $responseData['entity'] = $response->getEntity(); } diff --git a/src/Core/Schema/Serialization/ISerializator.php b/src/Core/Schema/Serialization/ISerializer.php similarity index 88% rename from src/Core/Schema/Serialization/ISerializator.php rename to src/Core/Schema/Serialization/ISerializer.php index b18c3482..324c6f9c 100644 --- a/src/Core/Schema/Serialization/ISerializator.php +++ b/src/Core/Schema/Serialization/ISerializer.php @@ -4,7 +4,7 @@ use Apitte\Core\Schema\SchemaBuilder; -interface ISerializator +interface ISerializer { public function serialize(SchemaBuilder $builder): mixed; diff --git a/src/Core/Schema/Validation/ControllerPathValidation.php b/src/Core/Schema/Validation/ControllerPathValidation.php index 1aacbfc6..95f717f4 100644 --- a/src/Core/Schema/Validation/ControllerPathValidation.php +++ b/src/Core/Schema/Validation/ControllerPathValidation.php @@ -33,14 +33,14 @@ protected function validateSlashes(SchemaBuilder $builder): void } // MUST: Starts with slash (/) - if (substr($path, 0, 1) !== '/') { + if (!str_starts_with($path, '/')) { throw new InvalidSchemaException( sprintf('@Path "%s" in "%s" must starts with "/" (slash).', $path, $controller->getClass()) ); } // MUST NOT: Ends with slash (/) - if (substr($path, -1, 1) === '/') { + if (str_ends_with($path, '/')) { throw new InvalidSchemaException( sprintf('@Path "%s" in "%s" must not ends with "/" (slash).', $path, $controller->getClass()) ); diff --git a/src/Core/Schema/Validation/GroupPathValidation.php b/src/Core/Schema/Validation/GroupPathValidation.php index f2feafab..55ca50b8 100644 --- a/src/Core/Schema/Validation/GroupPathValidation.php +++ b/src/Core/Schema/Validation/GroupPathValidation.php @@ -29,14 +29,14 @@ protected function validateSlashes(SchemaBuilder $builder): void } // MUST: Starts with slash (/) - if (substr($groupPath, 0, 1) !== '/') { + if (!str_starts_with($groupPath, '/')) { throw new InvalidSchemaException( sprintf('@Path "%s" in "%s" must starts with "/" (slash).', $groupPath, $controller->getClass()) ); } // MUST NOT: Ends with slash (/) - if (substr($groupPath, -1, 1) === '/') { + if (str_ends_with($groupPath, '/')) { throw new InvalidSchemaException( sprintf('@Path "%s" in "%s" must not ends with "/" (slash).', $groupPath, $controller->getClass()) ); @@ -78,6 +78,7 @@ protected function validateRegex(SchemaBuilder $builder): void // -> -_ // @regex https://regex101.com/r/APckUJ/3 $matches = Regex::matchAll($path, '#\{(.+)\}#U'); + if ($matches !== null) { foreach ($matches as $item) { $match = Regex::match($item[1], '#.*([^a-zA-Z0-9\-_]+).*#'); diff --git a/src/Core/Schema/Validation/NegotiationValidation.php b/src/Core/Schema/Validation/NegotiationValidation.php index 953ca16a..ac19e6da 100644 --- a/src/Core/Schema/Validation/NegotiationValidation.php +++ b/src/Core/Schema/Validation/NegotiationValidation.php @@ -16,6 +16,7 @@ public function validate(SchemaBuilder $builder): void $haveDefault = null; $takenSuffixes = []; + foreach ($method->getNegotiations() as $negotiation) { if ($negotiation->isDefault()) { if ($haveDefault !== null) { @@ -41,6 +42,7 @@ public function validate(SchemaBuilder $builder): void } $renderer = $negotiation->getRenderer(); + if ($renderer !== null) { if (!class_exists($renderer)) { throw new InvalidSchemaException(sprintf( @@ -52,6 +54,7 @@ public function validate(SchemaBuilder $builder): void } $reflection = new ReflectionClass($renderer); + if (!$reflection->hasMethod('__invoke')) { throw new InvalidSchemaException(sprintf( 'Negotiation renderer "%s" in "%s::%s()" does not implement __invoke(ApiRequest $request, ApiResponse $response, array $context): ApiResponse', diff --git a/src/Core/Schema/Validation/PathValidation.php b/src/Core/Schema/Validation/PathValidation.php index 2cb06e66..98789bcd 100644 --- a/src/Core/Schema/Validation/PathValidation.php +++ b/src/Core/Schema/Validation/PathValidation.php @@ -19,6 +19,7 @@ public function validate(SchemaBuilder $builder): void protected function validateRequirements(SchemaBuilder $builder): void { $controllers = $builder->getControllers(); + foreach ($controllers as $controller) { foreach ($controller->getMethods() as $method) { if ($method->getPath() === '') { @@ -45,7 +46,7 @@ protected function validateSlashes(SchemaBuilder $builder): void $path = $method->getPath(); // MUST: Starts with slash (/) - if (substr($path, 0, 1) !== '/') { + if (!str_starts_with($path, '/')) { throw (new InvalidSchemaException( sprintf( '@Path "%s" in "%s::%s()" must starts with "/" (slash).', @@ -59,7 +60,7 @@ protected function validateSlashes(SchemaBuilder $builder): void } // MUST NOT: Ends with slash (/), except single '/' path - if (substr($path, -1, 1) === '/' && strlen($path) > 1) { + if (str_ends_with($path, '/') && strlen($path) > 1) { throw (new InvalidSchemaException( sprintf( '@Path "%s" in "%s::%s()" must not ends with "/" (slash).', @@ -112,6 +113,7 @@ protected function validateRegex(SchemaBuilder $builder): void // -> -_ // @regex https://regex101.com/r/APckUJ/3 $matches = Regex::matchAll($path, '#\{(.+)\}#U'); + if ($matches !== null) { foreach ($matches as $item) { $match = Regex::match($item[1], '#.*([^a-zA-Z0-9\-_]+).*#'); diff --git a/src/Core/Schema/Validation/RequestParameterValidation.php b/src/Core/Schema/Validation/RequestParameterValidation.php index 68b59307..36afb49b 100644 --- a/src/Core/Schema/Validation/RequestParameterValidation.php +++ b/src/Core/Schema/Validation/RequestParameterValidation.php @@ -11,15 +11,13 @@ class RequestParameterValidation implements IValidation { - /** @var array */ - private array $allowedTypes; - /** * @param array $allowedTypes */ - public function __construct(array $allowedTypes = EndpointParameter::TYPES) + public function __construct( + private readonly array $allowedTypes = EndpointParameter::TYPES, + ) { - $this->allowedTypes = $allowedTypes; } public function validate(SchemaBuilder $builder): void diff --git a/src/Debug/Negotiation/Transformer/DebugDataTransformer.php b/src/Debug/Negotiation/Transformer/DebugDataTransformer.php index 7408ab4d..2f44b7c9 100644 --- a/src/Debug/Negotiation/Transformer/DebugDataTransformer.php +++ b/src/Debug/Negotiation/Transformer/DebugDataTransformer.php @@ -11,14 +11,11 @@ class DebugDataTransformer extends AbstractTransformer { - private int $maxDepth; - - private int $maxLength; - - public function __construct(int $maxDepth = 10, int $maxLength = 1500) + public function __construct( + private readonly int $maxDepth = 10, + private readonly int $maxLength = 1500, + ) { - $this->maxDepth = $maxDepth; - $this->maxLength = $maxLength; } /** diff --git a/src/Debug/Negotiation/Transformer/DebugTransformer.php b/src/Debug/Negotiation/Transformer/DebugTransformer.php index f289a005..bbac67db 100644 --- a/src/Debug/Negotiation/Transformer/DebugTransformer.php +++ b/src/Debug/Negotiation/Transformer/DebugTransformer.php @@ -11,14 +11,11 @@ class DebugTransformer extends AbstractTransformer { - private int $maxDepth; - - private int $maxLength; - - public function __construct(int $maxDepth = 10, int $maxLength = 1500) + public function __construct( + private readonly int $maxDepth = 10, + private readonly int $maxLength = 1500, + ) { - $this->maxDepth = $maxDepth; - $this->maxLength = $maxLength; } /** diff --git a/src/Debug/Tracy/Panel/ApiPanel.php b/src/Debug/Tracy/Panel/ApiPanel.php index 876630dc..60198d0c 100644 --- a/src/Debug/Tracy/Panel/ApiPanel.php +++ b/src/Debug/Tracy/Panel/ApiPanel.php @@ -8,11 +8,10 @@ class ApiPanel implements IBarPanel { - private Schema $schema; - - public function __construct(Schema $schema) + public function __construct( + private readonly Schema $schema, + ) { - $this->schema = $schema; } /** diff --git a/src/Middlewares/ApiMiddleware.php b/src/Middlewares/ApiMiddleware.php index 09336e6b..7b9c8858 100644 --- a/src/Middlewares/ApiMiddleware.php +++ b/src/Middlewares/ApiMiddleware.php @@ -15,14 +15,11 @@ class ApiMiddleware implements IMiddleware { - protected IDispatcher $dispatcher; - - protected IErrorHandler $errorHandler; - - public function __construct(IDispatcher $dispatcher, IErrorHandler $errorHandler) + public function __construct( + protected IDispatcher $dispatcher, + protected IErrorHandler $errorHandler, + ) { - $this->dispatcher = $dispatcher; - $this->errorHandler = $errorHandler; } public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface @@ -43,9 +40,7 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res } // Pass response to next middleware - $response = $next($request, $response); - - return $response; + return $next($request, $response); } } diff --git a/src/Negotiation/Decorator/ResponseEntityDecorator.php b/src/Negotiation/Decorator/ResponseEntityDecorator.php index ff0fe97c..9d4e153b 100644 --- a/src/Negotiation/Decorator/ResponseEntityDecorator.php +++ b/src/Negotiation/Decorator/ResponseEntityDecorator.php @@ -12,11 +12,10 @@ class ResponseEntityDecorator implements IResponseDecorator, IErrorDecorator { - private ContentNegotiation $negotiation; - - public function __construct(ContentNegotiation $negotiation) + public function __construct( + private readonly ContentNegotiation $negotiation, + ) { - $this->negotiation = $negotiation; } public function decorateError(ApiRequest $request, ApiResponse $response, ApiException $error): ApiResponse diff --git a/src/Negotiation/DefaultNegotiator.php b/src/Negotiation/DefaultNegotiator.php index 8af02616..4f463a77 100644 --- a/src/Negotiation/DefaultNegotiator.php +++ b/src/Negotiation/DefaultNegotiator.php @@ -32,9 +32,10 @@ public function negotiate(ApiRequest $request, ApiResponse $response, array $con // Early return if there's no endpoint $endpoint = $response->getEndpoint(); - if ($endpoint === null) - return null; + if ($endpoint === null) { + return null; + } // Get negotiations $negotiations = $endpoint->getNegotiations(); @@ -42,9 +43,9 @@ public function negotiate(ApiRequest $request, ApiResponse $response, array $con // Try default foreach ($negotiations as $negotiation) { // Skip non default negotiations - if (!$negotiation->isDefault()) - - continue; + if (!$negotiation->isDefault()) { + continue; + } // Normalize suffix for transformer $transformer = ltrim($negotiation->getSuffix(), '.'); diff --git a/src/Negotiation/FallbackNegotiator.php b/src/Negotiation/FallbackNegotiator.php index e0553a21..2dc5fab4 100644 --- a/src/Negotiation/FallbackNegotiator.php +++ b/src/Negotiation/FallbackNegotiator.php @@ -9,11 +9,10 @@ class FallbackNegotiator implements INegotiator { - protected ITransformer $transformer; - - public function __construct(ITransformer $transformer) + public function __construct( + protected ITransformer $transformer, + ) { - $this->transformer = $transformer; } /** diff --git a/src/Negotiation/Http/AbstractEntity.php b/src/Negotiation/Http/AbstractEntity.php index 767d0ee9..e323ac4d 100644 --- a/src/Negotiation/Http/AbstractEntity.php +++ b/src/Negotiation/Http/AbstractEntity.php @@ -5,11 +5,10 @@ abstract class AbstractEntity { - protected mixed $data; - - public function __construct(mixed $data = null) + public function __construct( + protected mixed $data = null, + ) { - $this->data = $data; } public function getData(): mixed diff --git a/src/Negotiation/Http/ArrayEntity.php b/src/Negotiation/Http/ArrayEntity.php index b12e754c..60700aae 100644 --- a/src/Negotiation/Http/ArrayEntity.php +++ b/src/Negotiation/Http/ArrayEntity.php @@ -24,7 +24,7 @@ public function __construct(array $data) /** * @param array $data - * @return static + * @return static */ public static function from(array $data): static { diff --git a/src/Negotiation/Http/MappingEntity.php b/src/Negotiation/Http/MappingEntity.php index d9b9f02f..50885e59 100644 --- a/src/Negotiation/Http/MappingEntity.php +++ b/src/Negotiation/Http/MappingEntity.php @@ -7,13 +7,11 @@ class MappingEntity extends AbstractEntity { - protected IResponseEntity $entity; - - public function __construct(IResponseEntity $entity) + public function __construct( + protected IResponseEntity $entity, + ) { parent::__construct(); - - $this->entity = $entity; } /** diff --git a/src/Negotiation/SuffixNegotiator.php b/src/Negotiation/SuffixNegotiator.php index f2904ae4..ebec87d1 100644 --- a/src/Negotiation/SuffixNegotiator.php +++ b/src/Negotiation/SuffixNegotiator.php @@ -32,9 +32,10 @@ public function negotiate(ApiRequest $request, ApiResponse $response, array $con // Early return if there's no endpoint $endpoint = $response->getEndpoint(); - if ($endpoint === null) - return null; + if ($endpoint === null) { + return null; + } // Get negotiations $negotiations = $endpoint->getNegotiations(); @@ -85,7 +86,7 @@ private function addTransformer(string $suffix, ITransformer $transformer): void */ private function match(string $path, string $suffix): bool { - return substr($path, -strlen($suffix)) === $suffix; + return str_ends_with($path, $suffix); } } diff --git a/src/Negotiation/Transformer/AbstractTransformer.php b/src/Negotiation/Transformer/AbstractTransformer.php index 8d8bcce7..31dbe855 100644 --- a/src/Negotiation/Transformer/AbstractTransformer.php +++ b/src/Negotiation/Transformer/AbstractTransformer.php @@ -12,6 +12,7 @@ abstract class AbstractTransformer implements ITransformer protected function getEntity(ApiResponse $response): AbstractEntity { $entity = $response->getEntity(); + if ($entity === null) { throw new InvalidStateException('Entity is required'); } diff --git a/src/Negotiation/Transformer/CsvTransformer.php b/src/Negotiation/Transformer/CsvTransformer.php index 678596bb..baeb05a1 100644 --- a/src/Negotiation/Transformer/CsvTransformer.php +++ b/src/Negotiation/Transformer/CsvTransformer.php @@ -58,7 +58,7 @@ private function convert(array $rows, string $delimiter = ',', string $enclosure foreach ($rows as $row) { foreach ($row as $item) { - if (is_array($item) || !is_scalar($item)) { + if (!is_scalar($item)) { return 'CSV need flat array'; } } diff --git a/src/Negotiation/Transformer/JsonTransformer.php b/src/Negotiation/Transformer/JsonTransformer.php index 700f60c1..916568fb 100644 --- a/src/Negotiation/Transformer/JsonTransformer.php +++ b/src/Negotiation/Transformer/JsonTransformer.php @@ -52,6 +52,7 @@ protected function extractException(ApiException $exception): array ]; $context = $exception->getContext(); + if ($context !== null) { $data['context'] = $context; } diff --git a/src/Negotiation/Transformer/JsonUnifyTransformer.php b/src/Negotiation/Transformer/JsonUnifyTransformer.php index 628e77e5..346eccd8 100644 --- a/src/Negotiation/Transformer/JsonUnifyTransformer.php +++ b/src/Negotiation/Transformer/JsonUnifyTransformer.php @@ -48,6 +48,7 @@ protected function transformException(ApiException $exception, ApiRequest $reque ]; $context = $exception->getContext(); + if ($context !== null) { $entityData['data']['context'] = $context; } diff --git a/src/Negotiation/Transformer/RendererTransformer.php b/src/Negotiation/Transformer/RendererTransformer.php index fe906375..2c528b4c 100644 --- a/src/Negotiation/Transformer/RendererTransformer.php +++ b/src/Negotiation/Transformer/RendererTransformer.php @@ -10,11 +10,10 @@ class RendererTransformer extends AbstractTransformer { - protected Container $container; - - public function __construct(Container $container) + public function __construct( + protected Container $container, + ) { - $this->container = $container; } /** diff --git a/src/OpenApi/DI/OpenApiPlugin.php b/src/OpenApi/DI/OpenApiPlugin.php index 43efb064..490819e9 100644 --- a/src/OpenApi/DI/OpenApiPlugin.php +++ b/src/OpenApi/DI/OpenApiPlugin.php @@ -52,6 +52,7 @@ public function loadPluginConfiguration(): void $schemaBuilder ->addSetup('addDefinition', [new Statement(BaseDefinition::class)]) ->addSetup('addDefinition', [$coreDefinition]); + foreach ($config->files as $file) { if (str_ends_with($file, '.neon')) { $schemaBuilder->addSetup('addDefinition', [new Statement(NeonDefinition::class, [$file])]); @@ -99,6 +100,7 @@ public function loadPluginConfiguration(): void public function afterPluginCompile(ClassType $class): void { $global = $this->compiler->getExtension()->getConfig(); + if (!$global->debug) { return; } @@ -106,6 +108,7 @@ public function afterPluginCompile(ClassType $class): void $config = $this->config; $initialize = $class->getMethod('initialize'); + if (!$config->swaggerUi->panel) { return; } diff --git a/src/OpenApi/SchemaBuilder.php b/src/OpenApi/SchemaBuilder.php index d9a8b41a..ba696fd4 100644 --- a/src/OpenApi/SchemaBuilder.php +++ b/src/OpenApi/SchemaBuilder.php @@ -30,6 +30,7 @@ public function build(): OpenApi protected function loadDefinitions(): array { $data = []; + foreach ($this->definitions as $definition) { $data = Helpers::merge($definition->load(), $data); } diff --git a/src/OpenApi/SchemaDefinition/ArrayDefinition.php b/src/OpenApi/SchemaDefinition/ArrayDefinition.php index 0d836489..860881ba 100644 --- a/src/OpenApi/SchemaDefinition/ArrayDefinition.php +++ b/src/OpenApi/SchemaDefinition/ArrayDefinition.php @@ -5,15 +5,13 @@ class ArrayDefinition implements IDefinition { - /** @var mixed[] */ - private array $data = []; - /** * @param mixed[] $data */ - public function __construct(array $data) + public function __construct( + private readonly array $data, + ) { - $this->data = $data; } /** diff --git a/src/OpenApi/SchemaDefinition/CoreDefinition.php b/src/OpenApi/SchemaDefinition/CoreDefinition.php index 74458b79..19669fee 100644 --- a/src/OpenApi/SchemaDefinition/CoreDefinition.php +++ b/src/OpenApi/SchemaDefinition/CoreDefinition.php @@ -13,14 +13,11 @@ class CoreDefinition implements IDefinition { - protected ApiSchema $schema; - - private IEntityAdapter $entityAdapter; - - public function __construct(ApiSchema $schema, IEntityAdapter $entityAdapter) + public function __construct( + protected ApiSchema $schema, + private readonly IEntityAdapter $entityAdapter, + ) { - $this->schema = $schema; - $this->entityAdapter = $entityAdapter; } /** @@ -29,6 +26,7 @@ public function __construct(ApiSchema $schema, IEntityAdapter $entityAdapter) public function load(): array { $data = ['paths' => []]; + foreach ($this->getEndpoints() as $endpoint) { foreach ($endpoint->getMethods() as $method) { $data['paths'][(string) $endpoint->getMask()][strtolower($method)] = $this->createOperation($endpoint); @@ -49,6 +47,7 @@ protected function createOperation(Endpoint $endpoint): array // Tags $tags = $this->getOperationTags($endpoint); + if ($tags !== []) { $operation['tags'] = array_keys($tags); } @@ -59,6 +58,7 @@ protected function createOperation(Endpoint $endpoint): array } $requestBody = $endpoint->getRequestBody(); + if ($requestBody !== null) { $operation['requestBody'] = $this->createRequestBody($requestBody); } @@ -68,9 +68,7 @@ protected function createOperation(Endpoint $endpoint): array // TODO deprecated // $operation->setDeprecated(false); - $operation = Helpers::merge($endpoint->getOpenApi()['method'] ?? [], $operation); - - return $operation; + return Helpers::merge($endpoint->getOpenApi()['method'] ?? [], $operation); } /** @@ -85,11 +83,13 @@ protected function createRequestBody(EndpointRequestBody $requestBody): array } $description = $requestBody->getDescription(); + if ($description !== null) { $requestBodyData['description'] = $description; } $entity = $requestBody->getEntity(); + if ($entity !== null) { $requestBodyData['content'] = [ // TODO resolve content types @@ -109,6 +109,7 @@ protected function createRequestBody(EndpointRequestBody $requestBody): array protected function createResponses(Endpoint $endpoint): array { $responses = []; + foreach ($endpoint->getResponses() as $response) { $responses[$response->getCode()] = $this->createResponse($response); } @@ -126,6 +127,7 @@ protected function createResponse(EndpointResponse $response): array ]; $entity = $response->getEntity(); + if ($entity !== null) { $responseData['content'] = [ // TODO resolve content types @@ -150,6 +152,7 @@ protected function createParameter(EndpointParameter $endpointParameter): array ]; $parameterDescription = $endpointParameter->getDescription(); + if ($parameterDescription !== null) { $parameter['description'] = $parameterDescription; } diff --git a/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php b/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php index 70cb408c..124df09e 100644 --- a/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php +++ b/src/OpenApi/SchemaDefinition/Entity/EntityAdapter.php @@ -36,6 +36,10 @@ public function getMetadata(string $type): array if ($usesUnionType || $usesIntersectionType) { $types = preg_split('#([&|])#', $type, -1, PREG_SPLIT_NO_EMPTY); + if ($types === false) { + throw new RuntimeException('Could not parse type ' . $type); + } + // Filter out duplicate definitions $types = array_map(fn (string $type): string => $this->normalizeType($type), $types); $types = array_unique($types); @@ -144,7 +148,7 @@ protected function getProperties(string $type): array foreach ($properties as $property) { $propertyType = $this->getPropertyType($property) ?? 'string'; - // Self-reference not supported + // Self-reference isn't supported if ($propertyType === $type) { $propertyType = 'object'; } @@ -205,7 +209,7 @@ private function getPropertyType(ReflectionProperty $property): ?string { $nativeType = null; - if (PHP_VERSION_ID >= 70400 && ($type = Type::fromReflection($property)) !== null) { + if (($type = Type::fromReflection($property)) !== null) { $nativeType = $this->getNativePropertyType($type, $property); // If type is array/mixed or union/intersection of it, try to get more information from annotations diff --git a/src/OpenApi/SchemaDefinition/JsonDefinition.php b/src/OpenApi/SchemaDefinition/JsonDefinition.php index 40a3319b..e7fda36c 100644 --- a/src/OpenApi/SchemaDefinition/JsonDefinition.php +++ b/src/OpenApi/SchemaDefinition/JsonDefinition.php @@ -8,11 +8,10 @@ class JsonDefinition implements IDefinition { - private string $file; - - public function __construct(string $file) + public function __construct( + private readonly string $file, + ) { - $this->file = $file; } /** @@ -21,11 +20,13 @@ public function __construct(string $file) public function load(): array { $content = file_get_contents($this->file); + if ($content === false) { throw new InvalidStateException('Cant read file ' . $this->file); } $decode = Json::decode($content, forceArrays: true); + if ($decode === false || $decode === null) { return []; } diff --git a/src/OpenApi/SchemaDefinition/NeonDefinition.php b/src/OpenApi/SchemaDefinition/NeonDefinition.php index aee60c20..fab4cd1d 100644 --- a/src/OpenApi/SchemaDefinition/NeonDefinition.php +++ b/src/OpenApi/SchemaDefinition/NeonDefinition.php @@ -8,11 +8,10 @@ class NeonDefinition implements IDefinition { - private string $file; - - public function __construct(string $file) + public function __construct( + private readonly string $file, + ) { - $this->file = $file; } /** @@ -21,11 +20,13 @@ public function __construct(string $file) public function load(): array { $input = file_get_contents($this->file); + if ($input === false) { throw new InvalidStateException('Cant read file ' . $this->file); } $decode = Neon::decode($input); + if ($decode === false || $decode === null) { return []; } diff --git a/src/OpenApi/SchemaDefinition/YamlDefinition.php b/src/OpenApi/SchemaDefinition/YamlDefinition.php index c11e18a3..24743116 100644 --- a/src/OpenApi/SchemaDefinition/YamlDefinition.php +++ b/src/OpenApi/SchemaDefinition/YamlDefinition.php @@ -7,11 +7,10 @@ class YamlDefinition implements IDefinition { - private string $file; - - public function __construct(string $file) + public function __construct( + private readonly string $file, + ) { - $this->file = $file; } /** @@ -20,6 +19,7 @@ public function __construct(string $file) public function load(): array { $decode = Yaml::parseFile($this->file); + if ($decode === false || $decode === null) { return []; } diff --git a/src/OpenApi/SchemaType/BaseSchemaType.php b/src/OpenApi/SchemaType/BaseSchemaType.php index 588cf21c..8be9ea1f 100644 --- a/src/OpenApi/SchemaType/BaseSchemaType.php +++ b/src/OpenApi/SchemaType/BaseSchemaType.php @@ -10,49 +10,38 @@ class BaseSchemaType implements ISchemaType public function createSchema(EndpointParameter $endpointParameter): Schema { - switch ($endpointParameter->getType()) { - case EndpointParameter::TYPE_STRING: - return new Schema( - [ - 'type' => 'string', - ] - ); - case EndpointParameter::TYPE_INTEGER: - return new Schema( - [ - 'type' => 'integer', - 'format' => 'int32', - ] - ); - case EndpointParameter::TYPE_FLOAT: - return new Schema( - [ - 'type' => 'float', - 'format' => 'float64', - ] - ); - case EndpointParameter::TYPE_BOOLEAN: - return new Schema( - [ - 'type' => 'boolean', - ] - ); - case EndpointParameter::TYPE_DATETIME: - return new Schema( - [ - 'type' => 'string', - 'format' => 'date-time', - ] - ); - case EndpointParameter::TYPE_ENUM: - return new Schema( - [ - 'type' => 'string', - ] - ); - default: - throw new UnknownSchemaType('Unknown endpoint parameter type ' . $endpointParameter->getType()); - } + return match ($endpointParameter->getType()) { + EndpointParameter::TYPE_STRING, + EndpointParameter::TYPE_ENUM => new Schema( + [ + 'type' => 'string', + ] + ), + EndpointParameter::TYPE_INTEGER => new Schema( + [ + 'type' => 'integer', + 'format' => 'int32', + ] + ), + EndpointParameter::TYPE_FLOAT => new Schema( + [ + 'type' => 'float', + 'format' => 'float64', + ] + ), + EndpointParameter::TYPE_BOOLEAN => new Schema( + [ + 'type' => 'boolean', + ] + ), + EndpointParameter::TYPE_DATETIME => new Schema( + [ + 'type' => 'string', + 'format' => 'date-time', + ] + ), + default => throw new UnknownSchemaType('Unknown endpoint parameter type ' . $endpointParameter->getType()), + }; } } diff --git a/src/Presenter/ApiPresenter.php b/src/Presenter/ApiPresenter.php index a7fa5bc0..41d8efc3 100644 --- a/src/Presenter/ApiPresenter.php +++ b/src/Presenter/ApiPresenter.php @@ -15,14 +15,11 @@ class ApiPresenter implements IPresenter { - private IApplication $application; - - private HttpRequest $request; - - public function __construct(IApplication $application, HttpRequest $request) + public function __construct( + private readonly IApplication $application, + private readonly HttpRequest $request, + ) { - $this->application = $application; - $this->request = $request; } public function run(Request $request): Response diff --git a/tests/Cases/Core/Schema/Serialization/ArraySerializator.phpt b/tests/Cases/Core/Schema/Serialization/ArraySerializator.phpt index 4e7932b8..d33a1815 100644 --- a/tests/Cases/Core/Schema/Serialization/ArraySerializator.phpt +++ b/tests/Cases/Core/Schema/Serialization/ArraySerializator.phpt @@ -7,13 +7,13 @@ use Apitte\Core\Schema\Endpoint; use Apitte\Core\Schema\EndpointParameter; use Apitte\Core\Schema\EndpointRequestBody; use Apitte\Core\Schema\SchemaBuilder; -use Apitte\Core\Schema\Serialization\ArraySerializator; +use Apitte\Core\Schema\Serialization\ArraySerializer; use Contributte\Tester\Toolkit; use Tester\Assert; // Serialize: success Toolkit::test(function (): void { - $serializator = new ArraySerializator(); + $serializator = new ArraySerializer(); $builder = new SchemaBuilder(); @@ -153,7 +153,7 @@ Toolkit::test(function (): void { // Serialize: Exception - duplicate mask parameter - in controller Toolkit::test(function (): void { - $serializator = new ArraySerializator(); + $serializator = new ArraySerializer(); $builder = new SchemaBuilder(); @@ -174,7 +174,7 @@ Toolkit::test(function (): void { // Serialize: Exception - duplicate mask parameter - in method Toolkit::test(function (): void { - $serializator = new ArraySerializator(); + $serializator = new ArraySerializer(); $builder = new SchemaBuilder(); @@ -192,7 +192,7 @@ Toolkit::test(function (): void { // Serialize: Exception - Parameter in mask is not defined in path Toolkit::test(function (): void { - $serializator = new ArraySerializator(); + $serializator = new ArraySerializer(); $builder = new SchemaBuilder(); diff --git a/tests/Cases/Core/Schema/Validation/PathValidation.phpt b/tests/Cases/Core/Schema/Validation/PathValidation.phpt index d9c3d98a..651eaf19 100644 --- a/tests/Cases/Core/Schema/Validation/PathValidation.phpt +++ b/tests/Cases/Core/Schema/Validation/PathValidation.phpt @@ -89,10 +89,11 @@ Toolkit::test(function (): void { $c1m1 = $c1->addMethod('foo1'); $c1m1->setPath('/{foo}/{bar}'); $c1m1->addHttpMethod('GET'); + try { $validator = new PathValidation(); $validator->validate($builder); - } catch (Throwable $e) { + } catch (Throwable) { Assert::fail('This is fail. Parameters are OK.'); } }); diff --git a/tests/Cases/Debug/Negotiation/Transformer/DebugDataTransformerTest.php b/tests/Cases/Debug/Negotiation/Transformer/DebugDataTransformerTest.php index c9b2949c..37c435da 100644 --- a/tests/Cases/Debug/Negotiation/Transformer/DebugDataTransformerTest.php +++ b/tests/Cases/Debug/Negotiation/Transformer/DebugDataTransformerTest.php @@ -22,7 +22,7 @@ $response = $transformer->transform($request, $response); $response->getBody()->rewind(); - $expected = version_compare(Debugger::VERSION, '2.8.0', '<') ? ' + $expected = version_compare(Debugger::Version, '2.8.0', '<') ? ' Apitte\Negotiation\Http\ArrayEntity %a% data protected => array (1) | foo => "bar" (3) diff --git a/tests/Fixtures/Controllers/AttributeMultiController.php b/tests/Fixtures/Controllers/AttributeMultiController.php index f27791ba..a4cd5a10 100644 --- a/tests/Fixtures/Controllers/AttributeMultiController.php +++ b/tests/Fixtures/Controllers/AttributeMultiController.php @@ -20,7 +20,7 @@ public function responses(): void } #[RequestParameter(name: 'name_value', type: 'type_value', in: 'path')] - #[RequestParameter(in: 'query', type: 'type_value_2', name: 'name_value_2')] + #[RequestParameter(name: 'name_value_2', type: 'type_value_2', in: 'query')] public function requestParameters(): void { // Tests