diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 5500844e0..ac68b34e8 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -24,17 +24,11 @@ jobs:
strategy:
matrix:
include:
- - php-version: 7.1
- composer-flags: "--prefer-lowest"
- - php-version: 7.2
- symfony-require: "^4.0"
- - php-version: 7.3
- symfony-require: "^5.0"
- - php-version: 7.4
- symfony-require: "^4.0"
- - php-version: 7.3
- symfony-require: "^5.0"
- php-version: 8.0
+ symfony-require: "^5.3"
+ - php-version: 8.0
+ symfony-require: "^6.0"
+ - php-version: 8.1
composer-flags: "--ignore-platform-reqs"
steps:
diff --git a/Annotation/Areas.php b/Annotation/Areas.php
index f4ce2f9da..821469812 100644
--- a/Annotation/Areas.php
+++ b/Annotation/Areas.php
@@ -17,7 +17,7 @@
final class Areas
{
/** @var string[] */
- private $areas;
+ private array $areas;
public function __construct(array $properties)
{
diff --git a/Annotation/Model.php b/Annotation/Model.php
index 5a258292a..f9751c42a 100644
--- a/Annotation/Model.php
+++ b/Annotation/Model.php
@@ -32,18 +32,15 @@ final class Model extends AbstractAnnotation
Parameter::class,
];
- /**
- * @var string
- */
- public $type;
+ public string $type;
/**
* @var string[]
*/
- public $groups;
+ public array $groups;
/**
* @var mixed[]
*/
- public $options;
+ public array $options;
}
diff --git a/Annotation/Security.php b/Annotation/Security.php
index 95bbc4b98..7d3b6793d 100644
--- a/Annotation/Security.php
+++ b/Annotation/Security.php
@@ -26,13 +26,10 @@ class Security extends AbstractAnnotation
public static $_required = ['name'];
- /**
- * @var string
- */
- public $name;
+ public string $name;
/**
* @var string[]
*/
- public $scopes = [];
+ public array $scopes = [];
}
diff --git a/ApiDocGenerator.php b/ApiDocGenerator.php
index 57b105987..0fbfe9ccf 100644
--- a/ApiDocGenerator.php
+++ b/ApiDocGenerator.php
@@ -20,44 +20,33 @@
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Analysis;
use OpenApi\Annotations\OpenApi;
+use OpenApi\Generator;
use Psr\Cache\CacheItemPoolInterface;
+use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerAwareTrait;
final class ApiDocGenerator
{
use LoggerAwareTrait;
- /** @var OpenApi */
- private $openApi;
-
- /** @var iterable|DescriberInterface[] */
- private $describers;
-
- /** @var iterable|ModelDescriberInterface[] */
- private $modelDescribers;
-
- /** @var CacheItemPoolInterface|null */
- private $cacheItemPool;
-
- /** @var string|null */
- private $cacheItemId;
+ private ?OpenApi $openApi = null;
/** @var string[] */
- private $alternativeNames = [];
+ private array $alternativeNames = [];
/** @var string[] */
- private $mediaTypes = ['json'];
+ private array $mediaTypes = ['json'];
/**
- * @param DescriberInterface[]|iterable $describers
+ * @param iterable|DescriberInterface[] $describers
* @param ModelDescriberInterface[]|iterable $modelDescribers
*/
- public function __construct($describers, $modelDescribers, CacheItemPoolInterface $cacheItemPool = null, string $cacheItemId = null)
- {
- $this->describers = $describers;
- $this->modelDescribers = $modelDescribers;
- $this->cacheItemPool = $cacheItemPool;
- $this->cacheItemId = $cacheItemId;
+ public function __construct(
+ private iterable $describers,
+ private iterable $modelDescribers,
+ private ?CacheItemPoolInterface $cacheItemPool = null,
+ private ?string $cacheItemId = null
+ ) {
}
public function setAlternativeNames(array $alternativeNames)
@@ -70,6 +59,9 @@ public function setMediaTypes(array $mediaTypes)
$this->mediaTypes = $mediaTypes;
}
+ /**
+ * @throws InvalidArgumentException
+ */
public function generate(): OpenApi
{
if (null !== $this->openApi) {
@@ -110,7 +102,7 @@ public function generate(): OpenApi
$defaultOperationIdProcessor = new DefaultOperationId();
$defaultOperationIdProcessor($analysis);
- $analysis->process();
+ $analysis->process((new Generator())->getProcessors());
$analysis->validate();
if (isset($item)) {
diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php
index c2d929fac..b48d788cd 100644
--- a/Command/DumpCommand.php
+++ b/Command/DumpCommand.php
@@ -20,23 +20,13 @@
class DumpCommand extends Command
{
- /**
- * @var RenderOpenApi
- */
- private $renderOpenApi;
-
- /**
- * @var mixed[]
- */
- private $defaultHtmlConfig = [
+ private array $defaultHtmlConfig = [
'assets_mode' => AssetsMode::CDN,
'swagger_ui_config' => [],
];
- public function __construct(RenderOpenApi $renderOpenApi)
+ public function __construct(private RenderOpenApi $renderOpenApi)
{
- $this->renderOpenApi = $renderOpenApi;
-
parent::__construct();
}
@@ -62,10 +52,7 @@ protected function configure()
;
}
- /**
- * @return int|void
- */
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
$area = $input->getOption('area');
$format = $input->getOption('format');
diff --git a/Controller/DocumentationController.php b/Controller/DocumentationController.php
index 020451a8a..773e0e463 100644
--- a/Controller/DocumentationController.php
+++ b/Controller/DocumentationController.php
@@ -18,23 +18,17 @@
final class DocumentationController
{
- /**
- * @var RenderOpenApi
- */
- private $renderOpenApi;
-
- public function __construct(RenderOpenApi $renderOpenApi)
+ public function __construct(private RenderOpenApi $renderOpenApi)
{
- $this->renderOpenApi = $renderOpenApi;
}
- public function __invoke(Request $request, $area = 'default')
+ public function __invoke(Request $request, $area = 'default'): JsonResponse
{
try {
return JsonResponse::fromJsonString(
$this->renderOpenApi->renderFromRequest($request, RenderOpenApi::JSON, $area)
);
- } catch (InvalidArgumentException $e) {
+ } catch (\InvalidArgumentException) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported as it isn\'t defined in config.', $area));
}
}
diff --git a/Controller/SwaggerUiController.php b/Controller/SwaggerUiController.php
index c5a32017d..f191c9303 100644
--- a/Controller/SwaggerUiController.php
+++ b/Controller/SwaggerUiController.php
@@ -11,7 +11,6 @@
namespace Nelmio\ApiDocBundle\Controller;
-use InvalidArgumentException;
use Nelmio\ApiDocBundle\Render\Html\AssetsMode;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\HttpFoundation\Request;
@@ -20,17 +19,11 @@
final class SwaggerUiController
{
- /**
- * @var RenderOpenApi
- */
- private $renderOpenApi;
-
- public function __construct(RenderOpenApi $renderOpenApi)
+ public function __construct(private RenderOpenApi $renderOpenApi)
{
- $this->renderOpenApi = $renderOpenApi;
}
- public function __invoke(Request $request, $area = 'default')
+ public function __invoke(Request $request, $area = 'default'): Response
{
try {
$response = new Response(
@@ -42,9 +35,9 @@ public function __invoke(Request $request, $area = 'default')
);
return $response->setCharset('UTF-8');
- } catch (InvalidArgumentException $e) {
+ } catch (\InvalidArgumentException) {
$advice = '';
- if (false !== strpos($area, '.json')) {
+ if (str_contains($area, '.json')) {
$advice = ' Since the area provided contains `.json`, the issue is likely caused by route priorities. Try switching the Swagger UI / the json documentation routes order.';
}
diff --git a/Controller/YamlDocumentationController.php b/Controller/YamlDocumentationController.php
index a935a2d4c..d17602012 100644
--- a/Controller/YamlDocumentationController.php
+++ b/Controller/YamlDocumentationController.php
@@ -11,7 +11,6 @@
namespace Nelmio\ApiDocBundle\Controller;
-use InvalidArgumentException;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -19,17 +18,11 @@
final class YamlDocumentationController
{
- /**
- * @var RenderOpenApi
- */
- private $renderOpenApi;
-
- public function __construct(RenderOpenApi $renderOpenApi)
+ public function __construct(private RenderOpenApi $renderOpenApi)
{
- $this->renderOpenApi = $renderOpenApi;
}
- public function __invoke(Request $request, $area = 'default')
+ public function __invoke(Request $request, $area = 'default'): Response
{
try {
$response = new Response(
@@ -39,7 +32,7 @@ public function __invoke(Request $request, $area = 'default')
);
return $response->setCharset('UTF-8');
- } catch (InvalidArgumentException $e) {
+ } catch (\InvalidArgumentException) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported as it isn\'t defined in config.', $area));
}
}
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 5a4c2e3a5..64befc8bb 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -16,16 +16,10 @@
final class Configuration implements ConfigurationInterface
{
- public function getConfigTreeBuilder()
+ public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('nelmio_api_doc');
-
- if (method_exists($treeBuilder, 'getRootNode')) {
- $rootNode = $treeBuilder->getRootNode();
- } else {
- // symfony < 4.2 support
- $rootNode = $treeBuilder->root('nelmio_api_doc');
- }
+ $rootNode = $treeBuilder->getRootNode();
$rootNode
->children()
diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php
index cd6a622d8..2040acbc7 100644
--- a/DependencyInjection/NelmioApiDocExtension.php
+++ b/DependencyInjection/NelmioApiDocExtension.php
@@ -152,14 +152,15 @@ public function load(array $configs, ContainerBuilder $container)
->setArgument(1, $config['media_types']);
}
- // ApiPlatform support
$bundles = $container->getParameter('kernel.bundles');
- if (!isset($bundles['TwigBundle'])) {
+ if (!isset($bundles['TwigBundle']) || !class_exists('Symfony\Component\Asset\Packages')) {
$container->removeDefinition('nelmio_api_doc.controller.swagger_ui');
$container->removeDefinition('nelmio_api_doc.render_docs.html');
$container->removeDefinition('nelmio_api_doc.render_docs.html.asset');
}
+
+ // ApiPlatform support
if (isset($bundles['ApiPlatformBundle']) && class_exists('ApiPlatform\Core\Documentation\Documentation')) {
$loader->load('api_platform.xml');
}
diff --git a/Describer/ApiPlatformDescriber.php b/Describer/ApiPlatformDescriber.php
index 2b837adde..fd46f3b89 100644
--- a/Describer/ApiPlatformDescriber.php
+++ b/Describer/ApiPlatformDescriber.php
@@ -20,7 +20,7 @@ final class ApiPlatformDescriber extends ExternalDocDescriber
public function __construct(Documentation $documentation, NormalizerInterface $normalizer)
{
if (!$normalizer->supportsNormalization($documentation, 'json')) {
- throw new \InvalidArgumentException(sprintf('Argument 2 passed to %s() must implement %s and support normalization of %s. The normalizer provided is an instance of %s.', __METHOD__, NormalizerInterface::class, Documentation::class, get_class($normalizer)));
+ throw new \InvalidArgumentException(sprintf('Argument 2 passed to %s() must implement %s and support normalization of %s. The normalizer provided is an instance of %s.', __METHOD__, NormalizerInterface::class, Documentation::class, $normalizer::class));
}
parent::__construct(function () use ($documentation, $normalizer) {
diff --git a/Describer/DefaultDescriber.php b/Describer/DefaultDescriber.php
index 5217d3c11..3812e3a03 100644
--- a/Describer/DefaultDescriber.php
+++ b/Describer/DefaultDescriber.php
@@ -13,6 +13,7 @@
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
/**
* Makes the swagger documentation valid even if there are missing fields.
@@ -26,22 +27,22 @@ public function describe(OA\OpenApi $api)
// Info
/** @var OA\Info $info */
$info = Util::getChild($api, OA\Info::class);
- if (OA\UNDEFINED === $info->title) {
+ if (Generator::UNDEFINED === $info->title) {
$info->title = '';
}
- if (OA\UNDEFINED === $info->version) {
+ if (Generator::UNDEFINED === $info->version) {
$info->version = '0.0.0';
}
// Paths
- if (OA\UNDEFINED === $api->paths) {
+ if (Generator::UNDEFINED === $api->paths) {
$api->paths = [];
}
foreach ($api->paths as $path) {
foreach (Util::OPERATIONS as $method) {
/** @var OA\Operation $operation */
$operation = $path->{$method};
- if (OA\UNDEFINED !== $operation && null !== $operation && (OA\UNDEFINED === $operation->responses || empty($operation->responses))) {
+ if (Generator::UNDEFINED !== $operation && null !== $operation && (Generator::UNDEFINED === $operation->responses || empty($operation->responses))) {
/** @var OA\Response $response */
$response = Util::getIndexedCollectionItem($operation, OA\Response::class, 'default');
$response->description = '';
diff --git a/Describer/ExternalDocDescriber.php b/Describer/ExternalDocDescriber.php
index e023a3349..edfaa83c2 100644
--- a/Describer/ExternalDocDescriber.php
+++ b/Describer/ExternalDocDescriber.php
@@ -18,15 +18,9 @@ class ExternalDocDescriber implements DescriberInterface
{
private $externalDoc;
- private $overwrite;
-
- /**
- * @param array|callable $externalDoc
- */
- public function __construct($externalDoc, bool $overwrite = false)
+ public function __construct(callable|array $externalDoc, private bool $overwrite = false)
{
$this->externalDoc = $externalDoc;
- $this->overwrite = $overwrite;
}
public function describe(OA\OpenApi $api)
diff --git a/Describer/ModelRegistryAwareTrait.php b/Describer/ModelRegistryAwareTrait.php
index 3bcdecac3..28eaaabe9 100644
--- a/Describer/ModelRegistryAwareTrait.php
+++ b/Describer/ModelRegistryAwareTrait.php
@@ -15,10 +15,7 @@
trait ModelRegistryAwareTrait
{
- /**
- * @var ModelRegistry
- */
- private $modelRegistry;
+ private ModelRegistry $modelRegistry;
public function setModelRegistry(ModelRegistry $modelRegistry)
{
diff --git a/Describer/OpenApiPhpDescriber.php b/Describer/OpenApiPhpDescriber.php
index 96aad2dc0..76160dac8 100644
--- a/Describer/OpenApiPhpDescriber.php
+++ b/Describer/OpenApiPhpDescriber.php
@@ -16,8 +16,9 @@
use Nelmio\ApiDocBundle\Annotation\Security;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use Nelmio\ApiDocBundle\Util\ControllerReflector;
-use OpenApi\Analyser;
+use Nelmio\ApiDocBundle\Util\SetsContextTrait;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -27,21 +28,20 @@ class_exists(OA\OpenApi::class);
final class OpenApiPhpDescriber
{
- private $routeCollection;
- private $controllerReflector;
- private $annotationReader;
- private $logger;
- private $overwrite;
-
- public function __construct(RouteCollection $routeCollection, ControllerReflector $controllerReflector, Reader $annotationReader, LoggerInterface $logger, bool $overwrite = false)
- {
- $this->routeCollection = $routeCollection;
- $this->controllerReflector = $controllerReflector;
- $this->annotationReader = $annotationReader;
- $this->logger = $logger;
- $this->overwrite = $overwrite;
+ use SetsContextTrait;
+
+ public function __construct(
+ private RouteCollection $routeCollection,
+ private ControllerReflector $controllerReflector,
+ private Reader $annotationReader,
+ private LoggerInterface $logger,
+ private bool $overwrite = false
+ ) {
}
+ /**
+ * @throws \Exception
+ */
public function describe(OA\OpenApi $api)
{
$classAnnotations = [];
@@ -52,11 +52,13 @@ public function describe(OA\OpenApi $api)
$path = Util::getPath($api, $path);
- Analyser::$context = Util::createContext(['nested' => $path], $path->_context);
- Analyser::$context->namespace = $method->getNamespaceName();
- Analyser::$context->class = $declaringClass->getShortName();
- Analyser::$context->method = $method->name;
- Analyser::$context->filename = $method->getFileName();
+ $context = Util::createContext(['nested' => $path], $path->_context);
+ $context->namespace = $method->getNamespaceName();
+ $context->class = $declaringClass->getShortName();
+ $context->method = $method->name;
+ $context->filename = $method->getFileName();
+
+ $this->setContext($context);
if (!array_key_exists($declaringClass->getName(), $classAnnotations)) {
$classAnnotations = array_filter($this->annotationReader->getClassAnnotations($declaringClass), function ($v) {
@@ -90,7 +92,7 @@ public function describe(OA\OpenApi $api)
if (!in_array($annotation->method, $httpMethods, true)) {
continue;
}
- if (OA\UNDEFINED !== $annotation->path && $path->path !== $annotation->path) {
+ if (Generator::UNDEFINED !== $annotation->path && $path->path !== $annotation->path) {
continue;
}
@@ -120,7 +122,7 @@ public function describe(OA\OpenApi $api)
!$annotation instanceof OA\Parameter &&
!$annotation instanceof OA\ExternalDocumentation
) {
- throw new \LogicException(sprintf('Using the annotation "%s" as a root annotation in "%s::%s()" is not allowed.', get_class($annotation), $method->getDeclaringClass()->name, $method->name));
+ throw new \LogicException(sprintf('Using the annotation "%s" as a root annotation in "%s::%s()" is not allowed.', $annotation::class, $method->getDeclaringClass()->name, $method->name));
}
$implicitAnnotations[] = $annotation;
@@ -135,14 +137,14 @@ public function describe(OA\OpenApi $api)
$operation->merge($implicitAnnotations);
$operation->mergeProperties($mergeProperties);
- if (OA\UNDEFINED === $operation->operationId) {
+ if (Generator::UNDEFINED === $operation->operationId) {
$operation->operationId = $httpMethod.'_'.$routeName;
}
}
}
- // Reset the Analyser after the parsing
- Analyser::$context = null;
+ // Reset the Generator after the parsing
+ $this->setContext(null);
}
private function getMethodsToParse(): \Generator
@@ -179,7 +181,7 @@ private function getSupportedHttpMethods(Route $route): array
private function normalizePath(string $path): string
{
- if ('.{_format}' === substr($path, -10)) {
+ if (str_ends_with($path, '.{_format}')) {
$path = substr($path, 0, -10);
}
diff --git a/Describer/RouteDescriber.php b/Describer/RouteDescriber.php
index 00b7ba729..a8c625a81 100644
--- a/Describer/RouteDescriber.php
+++ b/Describer/RouteDescriber.php
@@ -20,20 +20,14 @@ final class RouteDescriber implements DescriberInterface, ModelRegistryAwareInte
{
use ModelRegistryAwareTrait;
- private $routeCollection;
-
- private $controllerReflector;
-
- private $routeDescribers;
-
/**
* @param RouteDescriberInterface[]|iterable $routeDescribers
*/
- public function __construct(RouteCollection $routeCollection, ControllerReflector $controllerReflector, $routeDescribers)
- {
- $this->routeCollection = $routeCollection;
- $this->controllerReflector = $controllerReflector;
- $this->routeDescribers = $routeDescribers;
+ public function __construct(
+ private RouteCollection $routeCollection,
+ private ControllerReflector $controllerReflector,
+ private iterable $routeDescribers
+ ) {
}
public function describe(OA\OpenApi $api)
diff --git a/Exception/UndocumentedArrayItemsException.php b/Exception/UndocumentedArrayItemsException.php
index bedfcc27c..e875b481d 100644
--- a/Exception/UndocumentedArrayItemsException.php
+++ b/Exception/UndocumentedArrayItemsException.php
@@ -13,14 +13,8 @@
class UndocumentedArrayItemsException extends \LogicException
{
- private $class;
- private $path;
-
- public function __construct(string $class = null, string $path = '')
+ public function __construct(private ?string $class = null, private string $path = '')
{
- $this->class = $class;
- $this->path = $path;
-
$propertyName = '';
if (null !== $class) {
$propertyName = $class.'::';
@@ -30,12 +24,12 @@ public function __construct(string $class = null, string $path = '')
parent::__construct(sprintf('Property "%s" is an array, but its items type isn\'t specified. You can specify that by using the type `string[]` for instance or `@OA\Property(type="array", @OA\Items(type="string"))`.', $propertyName));
}
- public function getClass()
+ public function getClass(): ?string
{
return $this->class;
}
- public function getPath()
+ public function getPath(): string
{
return $this->path;
}
diff --git a/Form/Extension/DocumentationExtension.php b/Form/Extension/DocumentationExtension.php
index 2cc03a4b7..858b41c66 100644
--- a/Form/Extension/DocumentationExtension.php
+++ b/Form/Extension/DocumentationExtension.php
@@ -32,7 +32,7 @@ public function configureOptions(OptionsResolver $resolver)
->setAllowedTypes('documentation', ['array', 'bool']);
}
- public function getExtendedType()
+ public function getExtendedType(): FormType
{
return self::getExtendedTypes()[0];
}
diff --git a/Model/Model.php b/Model/Model.php
index a575bfef3..364eb3899 100644
--- a/Model/Model.php
+++ b/Model/Model.php
@@ -15,26 +15,14 @@
final class Model
{
- private $type;
-
- private $groups;
-
- private $options;
-
/**
* @param string[]|null $groups
*/
- public function __construct(Type $type, array $groups = null, array $options = null)
+ public function __construct(private Type $type, private ?array $groups = null, private ?array $options = null)
{
- $this->type = $type;
- $this->groups = $groups;
- $this->options = $options;
}
- /**
- * @return Type
- */
- public function getType()
+ public function getType(): Type
{
return $this->type;
}
@@ -42,7 +30,7 @@ public function getType()
/**
* @return string[]|null
*/
- public function getGroups()
+ public function getGroups(): ?array
{
return $this->groups;
}
@@ -52,10 +40,7 @@ public function getHash(): string
return md5(serialize([$this->type, $this->groups]));
}
- /**
- * @return mixed[]|null
- */
- public function getOptions()
+ public function getOptions(): ?array
{
return $this->options;
}
diff --git a/Model/ModelRegistry.php b/Model/ModelRegistry.php
index db346fb87..4ca3c3ed2 100644
--- a/Model/ModelRegistry.php
+++ b/Model/ModelRegistry.php
@@ -23,29 +23,22 @@ final class ModelRegistry
{
use LoggerAwareTrait;
- private $registeredModelNames = [];
-
- private $alternativeNames = [];
-
- private $unregistered = [];
-
- private $models = [];
-
- private $names = [];
-
- private $modelDescribers = [];
-
- private $api;
+ private array $registeredModelNames = [];
+ private array $alternativeNames = [];
+ private array $unregistered = [];
+ private array $models = [];
+ private array $names = [];
/**
* @param ModelDescriberInterface[]|iterable $modelDescribers
*
* @internal
*/
- public function __construct($modelDescribers, OA\OpenApi $api, array $alternativeNames = [])
- {
- $this->modelDescribers = $modelDescribers;
- $this->api = $api;
+ public function __construct(
+ private iterable $modelDescribers,
+ private OA\OpenApi $api,
+ array $alternativeNames = []
+ ) {
$this->logger = new NullLogger();
foreach (array_reverse($alternativeNames) as $alternativeName => $criteria) {
$this->alternativeNames[] = $model = new Model(new Type('object', false, $criteria['type']), $criteria['groups']);
@@ -188,31 +181,16 @@ private function typeToString(Type $type): string
private function getCollectionKeyTypes(Type $type): array
{
- // BC layer, this condition should be removed after removing support for symfony < 5.3
- if (!method_exists($type, 'getCollectionKeyTypes')) {
- return null !== $type->getCollectionKeyType() ? [$type->getCollectionKeyType()] : [];
- }
-
return $type->getCollectionKeyTypes();
}
private function getCollectionValueTypes(Type $type): array
{
- // BC layer, this condition should be removed after removing support for symfony < 5.3
- if (!method_exists($type, 'getCollectionValueTypes')) {
- return null !== $type->getCollectionValueType() ? [$type->getCollectionValueType()] : [];
- }
-
return $type->getCollectionValueTypes();
}
private function getCollectionValueType(Type $type): ?Type
{
- // BC layer, this condition should be removed after removing support for symfony < 5.3
- if (!method_exists($type, 'getCollectionValueTypes')) {
- return $type->getCollectionValueType();
- }
-
return $type->getCollectionValueTypes()[0] ?? null;
}
}
diff --git a/ModelDescriber/Annotations/AnnotationsReader.php b/ModelDescriber/Annotations/AnnotationsReader.php
index 62641abba..8489f94fd 100644
--- a/ModelDescriber/Annotations/AnnotationsReader.php
+++ b/ModelDescriber/Annotations/AnnotationsReader.php
@@ -20,18 +20,12 @@
*/
class AnnotationsReader
{
- private $annotationsReader;
- private $modelRegistry;
-
- private $phpDocReader;
- private $openApiAnnotationsReader;
- private $symfonyConstraintAnnotationReader;
+ private PropertyPhpDocReader $phpDocReader;
+ private OpenApiAnnotationsReader $openApiAnnotationsReader;
+ private SymfonyConstraintAnnotationReader $symfonyConstraintAnnotationReader;
public function __construct(Reader $annotationsReader, ModelRegistry $modelRegistry, array $mediaTypes)
{
- $this->annotationsReader = $annotationsReader;
- $this->modelRegistry = $modelRegistry;
-
$this->phpDocReader = new PropertyPhpDocReader();
$this->openApiAnnotationsReader = new OpenApiAnnotationsReader($annotationsReader, $modelRegistry, $mediaTypes);
$this->symfonyConstraintAnnotationReader = new SymfonyConstraintAnnotationReader($annotationsReader);
diff --git a/ModelDescriber/Annotations/OpenApiAnnotationsReader.php b/ModelDescriber/Annotations/OpenApiAnnotationsReader.php
index 020b8b11b..de5724c50 100644
--- a/ModelDescriber/Annotations/OpenApiAnnotationsReader.php
+++ b/ModelDescriber/Annotations/OpenApiAnnotationsReader.php
@@ -15,22 +15,23 @@
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use Nelmio\ApiDocBundle\OpenApiPhp\ModelRegister;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
-use OpenApi\Analyser;
+use Nelmio\ApiDocBundle\Util\SetsContextTrait;
use OpenApi\Analysis;
use OpenApi\Annotations as OA;
use OpenApi\Context;
+use OpenApi\Generator;
/**
* @internal
*/
class OpenApiAnnotationsReader
{
- private $annotationsReader;
- private $modelRegister;
+ use SetsContextTrait;
- public function __construct(Reader $annotationsReader, ModelRegistry $modelRegistry, array $mediaTypes)
+ private ModelRegister $modelRegister;
+
+ public function __construct(private Reader $annotationsReader, ModelRegistry $modelRegistry, array $mediaTypes)
{
- $this->annotationsReader = $annotationsReader;
$this->modelRegister = new ModelRegister($modelRegistry, $mediaTypes);
}
@@ -60,19 +61,20 @@ public function getPropertyName($reflection, string $default): string
return $default;
}
- return OA\UNDEFINED !== $oaProperty->property ? $oaProperty->property : $default;
+ return Generator::UNDEFINED !== $oaProperty->property ? $oaProperty->property : $default;
}
public function updateProperty($reflection, OA\Property $property, array $serializationGroups = null): void
{
// In order to have nicer errors
$declaringClass = $reflection->getDeclaringClass();
- Analyser::$context = new Context([
+
+ $this->setContext(new Context([
'namespace' => $declaringClass->getNamespaceName(),
'class' => $declaringClass->getShortName(),
'property' => $reflection->name,
'filename' => $declaringClass->getFileName(),
- ]);
+ ]));
/** @var OA\Property $oaProperty */
if ($reflection instanceof \ReflectionProperty && !$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflection, OA\Property::class)) {
@@ -80,7 +82,7 @@ public function updateProperty($reflection, OA\Property $property, array $serial
} elseif ($reflection instanceof \ReflectionMethod && !$oaProperty = $this->annotationsReader->getMethodAnnotation($reflection, OA\Property::class)) {
return;
}
- Analyser::$context = null;
+ $this->setContext(null);
// Read @Model annotations
$this->modelRegister->__invoke(new Analysis([$oaProperty], Util::createContext()), $serializationGroups);
diff --git a/ModelDescriber/Annotations/PropertyPhpDocReader.php b/ModelDescriber/Annotations/PropertyPhpDocReader.php
index 5234b45b0..16cf7e2bb 100644
--- a/ModelDescriber/Annotations/PropertyPhpDocReader.php
+++ b/ModelDescriber/Annotations/PropertyPhpDocReader.php
@@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
use phpDocumentor\Reflection\DocBlockFactory;
@@ -22,7 +23,7 @@
*/
class PropertyPhpDocReader
{
- private $docBlockFactory;
+ private DocBlockFactory $docBlockFactory;
public function __construct()
{
@@ -36,7 +37,7 @@ public function updateProperty($reflection, OA\Property $property): void
{
try {
$docBlock = $this->docBlockFactory->create($reflection);
- } catch (\Exception $e) {
+ } catch (\Exception) {
// ignore
return;
}
@@ -53,10 +54,10 @@ public function updateProperty($reflection, OA\Property $property): void
}
}
}
- if (OA\UNDEFINED === $property->title && $title) {
+ if (Generator::UNDEFINED === $property->title && $title) {
$property->title = $title;
}
- if (OA\UNDEFINED === $property->description && $docBlock->getDescription() && $docBlock->getDescription()->render()) {
+ if (Generator::UNDEFINED === $property->description && $docBlock->getDescription() && $docBlock->getDescription()->render()) {
$property->description = $docBlock->getDescription()->render();
}
}
diff --git a/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php b/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php
index 8fa31f6a7..d16d37ee5 100644
--- a/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php
+++ b/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php
@@ -14,6 +14,7 @@
use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints as Assert;
@@ -22,27 +23,16 @@
*/
class SymfonyConstraintAnnotationReader
{
- /**
- * @var Reader
- */
- private $annotationsReader;
-
- /**
- * @var OA\Schema
- */
- private $schema;
+ private OA\Schema $schema;
- public function __construct(Reader $annotationsReader)
+ public function __construct(private Reader $annotationsReader)
{
- $this->annotationsReader = $annotationsReader;
}
/**
* Update the given property and schema with defined Symfony constraints.
- *
- * @param \ReflectionProperty|\ReflectionMethod $reflection
*/
- public function updateProperty($reflection, OA\Property $property): void
+ public function updateProperty(\ReflectionMethod|\ReflectionProperty $reflection, OA\Property $property): void
{
foreach ($this->getAnnotations($reflection) as $outerAnnotation) {
$innerAnnotations = $outerAnnotation instanceof Assert\Compound
@@ -73,7 +63,7 @@ private function processPropertyAnnotations($reflection, OA\Property $property,
return;
}
- $existingRequiredFields = OA\UNDEFINED !== $this->schema->required ? $this->schema->required : [];
+ $existingRequiredFields = Generator::UNDEFINED !== $this->schema->required ? $this->schema->required : [];
$existingRequiredFields[] = $propertyName;
$this->schema->required = array_values(array_unique($existingRequiredFields));
@@ -131,7 +121,7 @@ private function getSchemaPropertyName(OA\Schema $property): ?string
}
foreach ($this->schema->properties as $schemaProperty) {
if ($schemaProperty === $property) {
- return OA\UNDEFINED !== $schemaProperty->property ? $schemaProperty->property : null;
+ return Generator::UNDEFINED !== $schemaProperty->property ? $schemaProperty->property : null;
}
}
@@ -146,17 +136,14 @@ private function appendPattern(OA\Schema $property, $newPattern): void
if (null === $newPattern) {
return;
}
- if (OA\UNDEFINED !== $property->pattern) {
+ if (Generator::UNDEFINED !== $property->pattern) {
$property->pattern = sprintf('%s, %s', $property->pattern, $newPattern);
} else {
$property->pattern = $newPattern;
}
}
- /**
- * @param \ReflectionProperty|\ReflectionMethod $reflection
- */
- private function applyEnumFromChoiceConstraint(OA\Schema $property, Assert\Choice $choice, $reflection): void
+ private function applyEnumFromChoiceConstraint(OA\Schema $property, Assert\Choice $choice, \ReflectionMethod|\ReflectionProperty $reflection): void
{
if ($choice->callback) {
$enumValues = call_user_func(is_array($choice->callback) ? $choice->callback : [$reflection->class, $choice->callback]);
@@ -172,10 +159,7 @@ private function applyEnumFromChoiceConstraint(OA\Schema $property, Assert\Choic
$setEnumOnThis->enum = array_values($enumValues);
}
- /**
- * @param \ReflectionProperty|\ReflectionMethod $reflection
- */
- private function getAnnotations($reflection): \Traversable
+ private function getAnnotations(\ReflectionMethod|\ReflectionProperty $reflection): \Traversable
{
if (\PHP_VERSION_ID >= 80000 && class_exists(Constraint::class)) {
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
diff --git a/ModelDescriber/BazingaHateoasModelDescriber.php b/ModelDescriber/BazingaHateoasModelDescriber.php
index 35e0eee32..3b5a0ba55 100644
--- a/ModelDescriber/BazingaHateoasModelDescriber.php
+++ b/ModelDescriber/BazingaHateoasModelDescriber.php
@@ -14,6 +14,8 @@
use Hateoas\Configuration\Metadata\ClassMetadata;
use Hateoas\Configuration\Relation;
use Hateoas\Serializer\Metadata\RelationPropertyMetadata;
+use Metadata\ClassHierarchyMetadata;
+use Metadata\MergeableClassMetadata;
use Metadata\MetadataFactoryInterface;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
@@ -26,13 +28,8 @@ class BazingaHateoasModelDescriber implements ModelDescriberInterface, ModelRegi
{
use ModelRegistryAwareTrait;
- private $factory;
- private $JMSModelDescriber;
-
- public function __construct(MetadataFactoryInterface $factory, JMSModelDescriber $JMSModelDescriber)
+ public function __construct(private MetadataFactoryInterface $factory, private JMSModelDescriber $JMSModelDescriber)
{
- $this->factory = $factory;
- $this->JMSModelDescriber = $JMSModelDescriber;
}
public function setModelRegistry(ModelRegistry $modelRegistry)
@@ -92,7 +89,7 @@ public function describe(Model $model, OA\Schema $schema): void
}
}
- private function getHateoasMetadata(Model $model)
+ private function getHateoasMetadata(Model $model): MergeableClassMetadata|ClassHierarchyMetadata|null
{
$className = $model->getType()->getClassName();
@@ -100,7 +97,7 @@ private function getHateoasMetadata(Model $model)
if ($metadata = $this->factory->getMetadataForClass($className)) {
return $metadata;
}
- } catch (\ReflectionException $e) {
+ } catch (\ReflectionException) {
}
return null;
diff --git a/ModelDescriber/FormModelDescriber.php b/ModelDescriber/FormModelDescriber.php
index 6bff0e443..30e5824cd 100644
--- a/ModelDescriber/FormModelDescriber.php
+++ b/ModelDescriber/FormModelDescriber.php
@@ -18,6 +18,7 @@
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\AnnotationsReader;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormConfigInterface;
@@ -34,15 +35,14 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry
{
use ModelRegistryAwareTrait;
- private $formFactory;
- private $doctrineReader;
- private $mediaTypes;
+ private ?array $mediaTypes;
- public function __construct(FormFactoryInterface $formFactory = null, Reader $reader = null, array $mediaTypes = null)
- {
- $this->formFactory = $formFactory;
- $this->doctrineReader = $reader;
- if (null === $reader) {
+ public function __construct(
+ private ?FormFactoryInterface $formFactory = null,
+ private ?Reader $doctrineReader = null,
+ array $mediaTypes = null
+ ) {
+ if (null === $doctrineReader) {
@trigger_error(sprintf('Not passing a doctrine reader to the constructor of %s is deprecated since version 3.8 and won\'t be allowed in version 5.', self::class), E_USER_DEPRECATED);
}
@@ -53,6 +53,9 @@ public function __construct(FormFactoryInterface $formFactory = null, Reader $re
$this->mediaTypes = $mediaTypes;
}
+ /**
+ * @throws \ReflectionException
+ */
public function describe(Model $model, OA\Schema $schema)
{
if (method_exists(AbstractType::class, 'setDefaultOptions')) {
@@ -90,7 +93,7 @@ private function parseForm(OA\Schema $schema, FormInterface $form)
$property = Util::getProperty($schema, $name);
if ($config->getRequired()) {
- $required = OA\UNDEFINED !== $schema->required ? $schema->required : [];
+ $required = Generator::UNDEFINED !== $schema->required ? $schema->required : [];
$required[] = $name;
$schema->required = $required;
@@ -100,7 +103,7 @@ private function parseForm(OA\Schema $schema, FormInterface $form)
$property->mergeProperties($config->getOption('documentation'));
}
- if (OA\UNDEFINED !== $property->type) {
+ if (Generator::UNDEFINED !== $property->type) {
continue; // Type manually defined
}
@@ -120,7 +123,7 @@ private function findFormType(FormConfigInterface $config, OA\Schema $property)
if (!$builtinFormType = $this->getBuiltinFormType($type)) {
// if form type is not builtin in Form component.
$model = new Model(
- new Type(Type::BUILTIN_TYPE_OBJECT, false, get_class($type->getInnerType())),
+ new Type(Type::BUILTIN_TYPE_OBJECT, false, $type->getInnerType()::class),
null,
$config->getOptions()
);
@@ -285,13 +288,10 @@ private function isBooleansArray(array $array): bool
return true;
}
- /**
- * @return ResolvedFormTypeInterface|null
- */
- private function getBuiltinFormType(ResolvedFormTypeInterface $type)
+ private function getBuiltinFormType(ResolvedFormTypeInterface $type): ?ResolvedFormTypeInterface
{
do {
- $class = get_class($type->getInnerType());
+ $class = $type->getInnerType()::class;
if (FormType::class === $class) {
return null;
@@ -301,7 +301,7 @@ private function getBuiltinFormType(ResolvedFormTypeInterface $type)
return $type;
}
- if (0 === strpos($class, 'Symfony\Component\Form\Extension\Core\Type\\')) {
+ if (str_starts_with($class, 'Symfony\Component\Form\Extension\Core\Type\\')) {
return $type;
}
} while ($type = $type->getParent());
diff --git a/ModelDescriber/JMSModelDescriber.php b/ModelDescriber/JMSModelDescriber.php
index c81519abd..b6a0ab279 100644
--- a/ModelDescriber/JMSModelDescriber.php
+++ b/ModelDescriber/JMSModelDescriber.php
@@ -14,6 +14,8 @@
use Doctrine\Common\Annotations\Reader;
use JMS\Serializer\Context;
use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
+use JMS\Serializer\Metadata\ClassMetadata;
+use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
use JMS\Serializer\SerializationContext;
use Metadata\MetadataFactoryInterface;
@@ -23,6 +25,7 @@
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\AnnotationsReader;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\PropertyInfo\Type;
/**
@@ -32,33 +35,18 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
{
use ModelRegistryAwareTrait;
- private $factory;
+ private array $contexts = [];
- private $namingStrategy;
+ private array $metadataStacks = [];
- private $doctrineReader;
-
- private $contexts = [];
-
- private $metadataStacks = [];
-
- private $mediaTypes;
-
- /**
- * @var array
- */
- private $propertyTypeUseGroupsCache = [];
+ private array $propertyTypeUseGroupsCache = [];
public function __construct(
- MetadataFactoryInterface $factory,
- Reader $reader,
- array $mediaTypes,
- ?PropertyNamingStrategyInterface $namingStrategy = null
+ private MetadataFactoryInterface $factory,
+ private Reader $doctrineReader,
+ private array $mediaTypes,
+ private ?PropertyNamingStrategyInterface $namingStrategy = null
) {
- $this->factory = $factory;
- $this->namingStrategy = $namingStrategy;
- $this->doctrineReader = $reader;
- $this->mediaTypes = $mediaTypes;
}
/**
@@ -67,7 +55,10 @@ public function __construct(
public function describe(Model $model, OA\Schema $schema)
{
$className = $model->getType()->getClassName();
+
+ /** @var ?ClassMetadata $metadata */
$metadata = $this->factory->getMetadataForClass($className);
+
if (null === $metadata) {
throw new \InvalidArgumentException(sprintf('No metadata found for class %s.', $className));
}
@@ -80,6 +71,8 @@ public function describe(Model $model, OA\Schema $schema)
$context = $this->getSerializationContext($model);
$context->pushClassMetadata($metadata);
+
+ /** @var PropertyMetadata $item */
foreach ($metadata->propertyMetadata as $item) {
// filter groups
if (null !== $context->getExclusionStrategy() && $context->getExclusionStrategy()->shouldSkipProperty($item, $context)) {
@@ -101,13 +94,13 @@ public function describe(Model $model, OA\Schema $schema)
if (null !== $item->getter) {
try {
$reflections[] = new \ReflectionMethod($item->class, $item->getter);
- } catch (\ReflectionException $ignored) {
+ } catch (\ReflectionException) {
}
}
if (null !== $item->setter) {
try {
$reflections[] = new \ReflectionMethod($item->class, $item->setter);
- } catch (\ReflectionException $ignored) {
+ } catch (\ReflectionException) {
}
}
@@ -134,7 +127,7 @@ public function describe(Model $model, OA\Schema $schema)
$annotationsReader->updateProperty($reflection, $property, $groups);
}
- if (OA\UNDEFINED !== $property->type || OA\UNDEFINED !== $property->ref) {
+ if (Generator::UNDEFINED !== $property->type || Generator::UNDEFINED !== $property->ref) {
$context->popPropertyMetadata();
continue;
@@ -180,7 +173,7 @@ public function getSerializationContext(Model $model): SerializationContext
return $context;
}
- private function computeGroups(Context $context, array $type = null)
+ private function computeGroups(Context $context, array $type = null): ?array
{
if (null === $type || true !== $this->propertyTypeUsesGroups($type)) {
return null;
@@ -210,7 +203,7 @@ public function supports(Model $model): bool
if ($this->factory->getMetadataForClass($className)) {
return true;
}
- } catch (\ReflectionException $e) {
+ } catch (\ReflectionException) {
}
return false;
@@ -278,7 +271,7 @@ public function describeItem(array $type, OA\Schema $property, Context $context)
}
}
- private function getNestedTypeInArray(array $type)
+ private function getNestedTypeInArray(array $type): ?array
{
if ('array' !== $type['name'] && 'ArrayCollection' !== $type['name']) {
return null;
@@ -295,10 +288,7 @@ private function getNestedTypeInArray(array $type)
return null;
}
- /**
- * @return bool|null
- */
- private function propertyTypeUsesGroups(array $type)
+ private function propertyTypeUsesGroups(array $type): ?bool
{
if (array_key_exists($type['name'], $this->propertyTypeUseGroupsCache)) {
return $this->propertyTypeUseGroupsCache[$type['name']];
@@ -317,7 +307,7 @@ private function propertyTypeUsesGroups(array $type)
$this->propertyTypeUseGroupsCache[$type['name']] = false;
return false;
- } catch (\ReflectionException $e) {
+ } catch (\ReflectionException) {
$this->propertyTypeUseGroupsCache[$type['name']] = null;
return null;
diff --git a/ModelDescriber/ObjectModelDescriber.php b/ModelDescriber/ObjectModelDescriber.php
index 0aa339a75..36118d88f 100644
--- a/ModelDescriber/ObjectModelDescriber.php
+++ b/ModelDescriber/ObjectModelDescriber.php
@@ -18,8 +18,8 @@
use Nelmio\ApiDocBundle\Model\Model;
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\AnnotationsReader;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
-use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
@@ -30,31 +30,18 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
use ModelRegistryAwareTrait;
use ApplyOpenApiDiscriminatorTrait;
- /** @var PropertyInfoExtractorInterface */
- private $propertyInfo;
- /** @var Reader */
- private $doctrineReader;
- /** @var PropertyDescriberInterface[] */
- private $propertyDescribers;
- /** @var string[] */
- private $mediaTypes;
- /** @var NameConverterInterface[] */
- private $nameConverter;
-
public function __construct(
- PropertyInfoExtractorInterface $propertyInfo,
- Reader $reader,
- iterable $propertyDescribers,
- array $mediaTypes,
- NameConverterInterface $nameConverter = null
+ private PropertyInfoExtractorInterface $propertyInfo,
+ private Reader $doctrineReader,
+ private iterable $propertyDescribers,
+ private array $mediaTypes,
+ private ?NameConverterInterface $nameConverter = null
) {
- $this->propertyInfo = $propertyInfo;
- $this->doctrineReader = $reader;
- $this->propertyDescribers = $propertyDescribers;
- $this->mediaTypes = $mediaTypes;
- $this->nameConverter = $nameConverter;
}
+ /**
+ * @throws \ReflectionException
+ */
public function describe(Model $model, OA\Schema $schema)
{
$schema->type = 'object';
@@ -72,7 +59,7 @@ public function describe(Model $model, OA\Schema $schema)
$annotationsReader->updateDefinition($reflClass, $schema);
$discriminatorMap = $this->doctrineReader->getClassAnnotation($reflClass, DiscriminatorMap::class);
- if ($discriminatorMap && OA\UNDEFINED === $schema->discriminator) {
+ if ($discriminatorMap && Generator::UNDEFINED === $schema->discriminator) {
$this->applyOpenApiDiscriminator(
$model,
$schema,
@@ -114,7 +101,7 @@ public function describe(Model $model, OA\Schema $schema)
}
// If type manually defined
- if (OA\UNDEFINED !== $property->type || OA\UNDEFINED !== $property->ref) {
+ if (Generator::UNDEFINED !== $property->type || Generator::UNDEFINED !== $property->ref) {
continue;
}
@@ -157,6 +144,8 @@ private function camelize(string $string): string
/**
* @param Type[] $types
+ *
+ * @throws \Exception
*/
private function describeProperty(array $types, Model $model, OA\Schema $property, string $propertyName)
{
diff --git a/OpenApiPhp/DefaultOperationId.php b/OpenApiPhp/DefaultOperationId.php
index b923b8c86..0a30fce1b 100644
--- a/OpenApiPhp/DefaultOperationId.php
+++ b/OpenApiPhp/DefaultOperationId.php
@@ -13,6 +13,7 @@
use OpenApi\Analysis;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
/**
* Disable the OperationId processor from zircote/swagger-php as it breaks our documentation by setting non-unique operation ids.
@@ -27,7 +28,7 @@ public function __invoke(Analysis $analysis)
$allOperations = $analysis->getAnnotationsOfType(OA\Operation::class);
foreach ($allOperations as $operation) {
- if (OA\UNDEFINED === $operation->operationId) {
+ if (Generator::UNDEFINED === $operation->operationId) {
$operation->operationId = null;
}
}
diff --git a/OpenApiPhp/ModelRegister.php b/OpenApiPhp/ModelRegister.php
index 16dd6d0c1..1f003c1f3 100644
--- a/OpenApiPhp/ModelRegister.php
+++ b/OpenApiPhp/ModelRegister.php
@@ -25,16 +25,8 @@
*/
final class ModelRegister
{
- /** @var ModelRegistry */
- private $modelRegistry;
-
- /** @var string[] */
- private $mediaTypes;
-
- public function __construct(ModelRegistry $modelRegistry, array $mediaTypes)
+ public function __construct(private ModelRegistry $modelRegistry, private array $mediaTypes)
{
- $this->modelRegistry = $modelRegistry;
- $this->mediaTypes = $mediaTypes;
}
public function __invoke(Analysis $analysis, array $parentGroups = null)
@@ -54,7 +46,7 @@ public function __invoke(Analysis $analysis, array $parentGroups = null)
// Misusage of ::$ref
if (($annotation instanceof OA\Response || $annotation instanceof OA\RequestBody) && $annotation->ref instanceof ModelAnnotation) {
- throw new \InvalidArgumentException(sprintf('Using @Model inside @%s::$ref is not allowed. You should use ::$ref with @Property, @Parameter, @Schema, @Items but within @Response or @RequestBody you should put @Model directly at the root of the annotation : `@Response(..., @Model(...))`.', get_class($annotation)));
+ throw new \InvalidArgumentException(sprintf('Using @Model inside @%s::$ref is not allowed. You should use ::$ref with @Property, @Parameter, @Schema, @Items but within @Response or @RequestBody you should put @Model directly at the root of the annotation : `@Response(..., @Model(...))`.', $annotation::class));
}
// Implicit usages
@@ -82,7 +74,7 @@ public function __invoke(Analysis $analysis, array $parentGroups = null)
}
if (!$annotation instanceof OA\Parameter) {
- throw new \InvalidArgumentException(sprintf("@Model annotation can't be nested with an annotation of type @%s.", get_class($annotation)));
+ throw new \InvalidArgumentException(sprintf("@Model annotation can't be nested with an annotation of type @%s.", $annotation::class));
}
if ($annotation->schema instanceof OA\Schema && 'array' === $annotation->schema->type) {
@@ -128,7 +120,7 @@ private function detach(ModelAnnotation $model, OA\AbstractAnnotation $annotatio
private function createType(string $type): Type
{
- if ('[]' === substr($type, -2)) {
+ if (str_ends_with($type, '[]')) {
return new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, $this->createType(substr($type, 0, -2)));
}
@@ -152,18 +144,11 @@ private function createContentForMediaType(
OA\AbstractAnnotation $annotation,
Analysis $analysis
) {
- switch ($type) {
- case 'json':
- $modelAnnotation = new OA\JsonContent($properties);
-
- break;
- case 'xml':
- $modelAnnotation = new OA\XmlContent($properties);
-
- break;
- default:
- throw new \InvalidArgumentException(sprintf("@Model annotation is not compatible with the media types '%s'. It must be one of 'json' or 'xml'.", implode(',', $this->mediaTypes)));
- }
+ $modelAnnotation = match ($type) {
+ 'json' => new OA\JsonContent($properties),
+ 'xml' => new OA\XmlContent($properties),
+ default => throw new \InvalidArgumentException(sprintf("@Model annotation is not compatible with the media types '%s'. It must be one of 'json' or 'xml'.", implode(',', $this->mediaTypes))),
+ };
$annotation->merge([$modelAnnotation]);
$analysis->addAnnotation($modelAnnotation, $properties['_context']);
diff --git a/OpenApiPhp/Util.php b/OpenApiPhp/Util.php
index ba97edeb2..1c1100f31 100644
--- a/OpenApiPhp/Util.php
+++ b/OpenApiPhp/Util.php
@@ -13,7 +13,7 @@
use OpenApi\Annotations as OA;
use OpenApi\Context;
-use const OpenApi\UNDEFINED;
+use OpenApi\Generator;
/**
* Class Util.
@@ -46,7 +46,7 @@
* @see \Nelmio\ApiDocBundle\OpenApiPhp\Util::createContext()
*
* The merge method @see \Nelmio\ApiDocBundle\OpenApiPhp\Util::merge() has the main purpose to be able
- * to merge properties from an deeply nested array of Annotation properties in the structure of a
+ * to merge properties from a deeply nested array of Annotation properties in the structure of a
* generated swagger json decoded array.
*/
final class Util
@@ -62,12 +62,10 @@ final class Util
* Return an existing PathItem object from $api->paths[] having its member path set to $path.
* Create, add to $api->paths[] and return this new PathItem object and set the property if none found.
*
- * @see OA\OpenApi::$paths
* @see OA\PathItem::path
- *
- * @param string $path
+ * @see OA\OpenApi::$paths
*/
- public static function getPath(OA\OpenApi $api, $path): OA\PathItem
+ public static function getPath(OA\OpenApi $api, string $path): OA\PathItem
{
return self::getIndexedCollectionItem($api, OA\PathItem::class, $path);
}
@@ -76,12 +74,10 @@ public static function getPath(OA\OpenApi $api, $path): OA\PathItem
* Return an existing Schema object from $api->components->schemas[] having its member schema set to $schema.
* Create, add to $api->components->schemas[] and return this new Schema object and set the property if none found.
*
- * @param string $schema
- *
* @see OA\Schema::$schema
* @see OA\Components::$schemas
*/
- public static function getSchema(OA\OpenApi $api, $schema): OA\Schema
+ public static function getSchema(OA\OpenApi $api, string $schema): OA\Schema
{
if (!$api->components instanceof OA\Components) {
$api->components = new OA\Components([]);
@@ -97,12 +93,10 @@ public static function getSchema(OA\OpenApi $api, $schema): OA\Schema
* Create, add to $schema->properties[] and return this new Property object
* and set the property if none found.
*
- * @see OA\Schema::$properties
* @see OA\Property::$property
- *
- * @param string $property
+ * @see OA\Schema::$properties
*/
- public static function getProperty(OA\Schema $schema, $property): OA\Property
+ public static function getProperty(OA\Schema $schema, string $property): OA\Property
{
return self::getIndexedCollectionItem($schema, OA\Property::class, $property);
}
@@ -111,17 +105,15 @@ public static function getProperty(OA\Schema $schema, $property): OA\Property
* Return an existing Operation from $path->{$method}
* or create, set $path->{$method} and return this new Operation object.
*
- * @see OA\PathItem::$get
* @see OA\PathItem::$post
* @see OA\PathItem::$put
* @see OA\PathItem::$patch
* @see OA\PathItem::$delete
* @see OA\PathItem::$options
* @see OA\PathItem::$head
- *
- * @param string $method
+ * @see OA\PathItem::$get
*/
- public static function getOperation(OA\PathItem $path, $method): OA\Operation
+ public static function getOperation(OA\PathItem $path, string $method): OA\Operation
{
$class = array_keys($path::$_nested, \strtolower($method), true)[0];
@@ -138,11 +130,8 @@ public static function getOperation(OA\PathItem $path, $method): OA\Operation
* @see OA\Operation::$parameters
* @see OA\Parameter::$name
* @see OA\Parameter::$in
- *
- * @param string $name
- * @param string $in
*/
- public static function getOperationParameter(OA\Operation $operation, $name, $in): OA\Parameter
+ public static function getOperationParameter(OA\Operation $operation, string $name, string $in): OA\Parameter
{
return self::getCollectionItem($operation, OA\Parameter::class, ['name' => $name, 'in' => $in]);
}
@@ -155,15 +144,16 @@ public static function getOperationParameter(OA\Operation $operation, $name, $in
* it is expected to be a string nested property.
*
* @see OA\AbstractAnnotation::$_nested
- *
- * @param $class
*/
- public static function getChild(OA\AbstractAnnotation $parent, $class, array $properties = []): OA\AbstractAnnotation
- {
+ public static function getChild(
+ OA\AbstractAnnotation $parent,
+ string $class,
+ array $properties = []
+ ): OA\AbstractAnnotation {
$nested = $parent::$_nested;
$property = $nested[$class];
- if (null === $parent->{$property} || UNDEFINED === $parent->{$property}) {
+ if (null === $parent->{$property} || Generator::UNDEFINED === $parent->{$property}) {
$parent->{$property} = self::createChild($parent, $class, $properties);
}
@@ -181,18 +171,19 @@ public static function getChild(OA\AbstractAnnotation $parent, $class, array $pr
* it is expected to be a single value array nested Annotation.
*
* @see OA\AbstractAnnotation::$_nested
- *
- * @param string $class
*/
- public static function getCollectionItem(OA\AbstractAnnotation $parent, $class, array $properties = []): OA\AbstractAnnotation
- {
+ public static function getCollectionItem(
+ OA\AbstractAnnotation $parent,
+ string $class,
+ array $properties = []
+ ): OA\AbstractAnnotation {
$key = null;
$nested = $parent::$_nested;
$collection = $nested[$class][0];
if (!empty($properties)) {
$key = self::searchCollectionItem(
- $parent->{$collection} && UNDEFINED !== $parent->{$collection} ? $parent->{$collection} : [],
+ $parent->{$collection} && Generator::UNDEFINED !== $parent->{$collection} ? $parent->{$collection} : [],
$properties
);
}
@@ -214,17 +205,17 @@ public static function getCollectionItem(OA\AbstractAnnotation $parent, $class,
* with the second value being the mapping index $property.
*
* @see OA\AbstractAnnotation::$_nested
- *
- * @param string $class
- * @param mixed $value
*/
- public static function getIndexedCollectionItem(OA\AbstractAnnotation $parent, $class, $value): OA\AbstractAnnotation
- {
+ public static function getIndexedCollectionItem(
+ OA\AbstractAnnotation $parent,
+ string $class,
+ mixed $value
+ ): OA\AbstractAnnotation {
$nested = $parent::$_nested;
[$collection, $property] = $nested[$class];
$key = self::searchIndexedCollectionItem(
- $parent->{$collection} && UNDEFINED !== $parent->{$collection} ? $parent->{$collection} : [],
+ $parent->{$collection} && Generator::UNDEFINED !== $parent->{$collection} ? $parent->{$collection} : [],
$property,
$value
);
@@ -239,10 +230,8 @@ public static function getIndexedCollectionItem(OA\AbstractAnnotation $parent, $
/**
* Search for an Annotation within $collection that has all members set
* to the respective values in the associative array $properties.
- *
- * @return int|string|null
*/
- public static function searchCollectionItem(array $collection, array $properties)
+ public static function searchCollectionItem(array $collection, array $properties): int|string|null
{
foreach ($collection ?: [] as $i => $child) {
foreach ($properties as $k => $prop) {
@@ -259,13 +248,8 @@ public static function searchCollectionItem(array $collection, array $properties
/**
* Search for an Annotation within the $collection that has its member $index set to $value.
- *
- * @param string $member
- * @param mixed $value
- *
- * @return false|int|string
*/
- public static function searchIndexedCollectionItem(array $collection, $member, $value)
+ public static function searchIndexedCollectionItem(array $collection, string $member, mixed $value): int|string|false
{
return array_search($value, array_column($collection, $member), true);
}
@@ -273,13 +257,14 @@ public static function searchIndexedCollectionItem(array $collection, $member, $
/**
* Create a new Object of $class with members $properties within $parent->{$collection}[]
* and return the created index.
- *
- * @param string $collection
- * @param string $class
*/
- public static function createCollectionItem(OA\AbstractAnnotation $parent, $collection, $class, array $properties = []): int
- {
- if (UNDEFINED === $parent->{$collection}) {
+ public static function createCollectionItem(
+ OA\AbstractAnnotation $parent,
+ string $collection,
+ string $class,
+ array $properties = []
+ ): int {
+ if (Generator::UNDEFINED === $parent->{$collection}) {
$parent->{$collection} = [];
}
@@ -292,12 +277,13 @@ public static function createCollectionItem(OA\AbstractAnnotation $parent, $coll
/**
* Create a new Object of $class with members $properties and set the context parent to be $parent.
*
- * @param string $class
- *
* @throws \InvalidArgumentException at an attempt to pass in properties that are found in $parent::$_nested
*/
- public static function createChild(OA\AbstractAnnotation $parent, $class, array $properties = []): OA\AbstractAnnotation
- {
+ public static function createChild(
+ OA\AbstractAnnotation $parent,
+ string $class,
+ array $properties = []
+ ): OA\AbstractAnnotation {
$nesting = self::getNestingIndexes($class);
if (!empty(array_intersect(array_keys($properties), $nesting))) {
@@ -316,8 +302,6 @@ public static function createChild(OA\AbstractAnnotation $parent, $class, array
*/
public static function createContext(array $properties = [], Context $parent = null): Context
{
- $properties['comment'] = ''; // TODO: remove this when https://github.com/zircote/swagger-php/commit/708a25208797ca05ebeae572bbccad8b13de14d8 is released
-
return new Context($properties, $parent);
}
@@ -326,11 +310,12 @@ public static function createContext(array $properties = [], Context $parent = n
*
* The main purpose is to create a Swagger Object from array config values
* in the structure of a json serialized Swagger object.
- *
- * @param array|\ArrayObject|OA\AbstractAnnotation $from
*/
- public static function merge(OA\AbstractAnnotation $annotation, $from, bool $overwrite = false)
- {
+ public static function merge(
+ OA\AbstractAnnotation $annotation,
+ array|\ArrayObject|OA\AbstractAnnotation $from,
+ bool $overwrite = false
+ ) {
if (\is_array($from)) {
self::mergeFromArray($annotation, $from, $overwrite);
} elseif (\is_a($from, OA\AbstractAnnotation::class)) {
@@ -346,7 +331,7 @@ private static function mergeFromArray(OA\AbstractAnnotation $annotation, array
{
$done = [];
- $defaults = \get_class_vars(\get_class($annotation));
+ $defaults = \get_class_vars($annotation::class);
foreach ($annotation::$_nested as $className => $propertyName) {
if (\is_string($propertyName)) {
@@ -362,7 +347,7 @@ private static function mergeFromArray(OA\AbstractAnnotation $annotation, array
} elseif (\array_key_exists($propertyName[0], $properties)) {
$collection = $propertyName[0];
$property = $propertyName[1] ?? null;
- self::mergeCollection($annotation, $className, $collection, $property, $properties[$collection], $overwrite);
+ self::mergeCollection($annotation, $className, $property, $properties[$collection], $overwrite);
$done[] = $collection;
}
}
@@ -389,7 +374,7 @@ private static function mergeChild(OA\AbstractAnnotation $annotation, $className
self::merge(self::getChild($annotation, $className), $value, $overwrite);
}
- private static function mergeCollection(OA\AbstractAnnotation $annotation, $className, $collection, $property, $items, bool $overwrite)
+ private static function mergeCollection(OA\AbstractAnnotation $annotation, $className, $property, $items, bool $overwrite)
{
if (null !== $property) {
foreach ($items as $prop => $value) {
@@ -415,10 +400,10 @@ private static function mergeCollection(OA\AbstractAnnotation $annotation, $clas
private static function mergeTyped(OA\AbstractAnnotation $annotation, $propertyName, $type, array $properties, array $defaults, bool $overwrite)
{
- if (\is_string($type) && 0 === strpos($type, '[')) {
+ if (\is_string($type) && str_starts_with($type, '[')) {
$innerType = substr($type, 1, -1);
- if (!$annotation->{$propertyName} || UNDEFINED === $annotation->{$propertyName}) {
+ if (!$annotation->{$propertyName} || Generator::UNDEFINED === $annotation->{$propertyName}) {
$annotation->{$propertyName} = [];
}
diff --git a/PropertyDescriber/ArrayPropertyDescriber.php b/PropertyDescriber/ArrayPropertyDescriber.php
index ee89c1728..8ecac527a 100644
--- a/PropertyDescriber/ArrayPropertyDescriber.php
+++ b/PropertyDescriber/ArrayPropertyDescriber.php
@@ -22,20 +22,17 @@ class ArrayPropertyDescriber implements PropertyDescriberInterface, ModelRegistr
use ModelRegistryAwareTrait;
use NullablePropertyTrait;
- /** @var PropertyDescriberInterface[] */
- private $propertyDescribers;
-
- public function __construct(iterable $propertyDescribers = [])
+ /**
+ * @param PropertyDescriberInterface[] $propertyDescribers
+ */
+ public function __construct(private iterable $propertyDescribers = [])
{
- $this->propertyDescribers = $propertyDescribers;
}
public function describe(array $types, OA\Schema $property, array $groups = null)
{
- // BC layer for symfony < 5.3
- $type = method_exists($types[0], 'getCollectionValueTypes') ?
- ($types[0]->getCollectionValueTypes()[0] ?? null) :
- $types[0]->getCollectionValueType();
+ $type = $types[0]->getCollectionValueTypes()[0] ?? null;
+
if (null === $type) {
throw new UndocumentedArrayItemsException();
}
diff --git a/PropertyDescriber/CompoundPropertyDescriber.php b/PropertyDescriber/CompoundPropertyDescriber.php
index ba6cf6c27..a06df12e3 100644
--- a/PropertyDescriber/CompoundPropertyDescriber.php
+++ b/PropertyDescriber/CompoundPropertyDescriber.php
@@ -15,22 +15,19 @@
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
class CompoundPropertyDescriber implements PropertyDescriberInterface, ModelRegistryAwareInterface
{
use ModelRegistryAwareTrait;
- /** @var PropertyDescriberInterface[] */
- private $propertyDescribers;
-
- public function __construct(iterable $propertyDescribers)
+ public function __construct(private iterable $propertyDescribers)
{
- $this->propertyDescribers = $propertyDescribers;
}
public function describe(array $types, OA\Schema $property, array $groups = null)
{
- $property->oneOf = OA\UNDEFINED !== $property->oneOf ? $property->oneOf : [];
+ $property->oneOf = Generator::UNDEFINED !== $property->oneOf ? $property->oneOf : [];
foreach ($types as $type) {
$property->oneOf[] = $schema = Util::createChild($property, OA\Schema::class, []);
diff --git a/PropertyDescriber/ObjectPropertyDescriber.php b/PropertyDescriber/ObjectPropertyDescriber.php
index ef1aabc4d..d29e3f91f 100644
--- a/PropertyDescriber/ObjectPropertyDescriber.php
+++ b/PropertyDescriber/ObjectPropertyDescriber.php
@@ -28,11 +28,8 @@ public function describe(array $types, OA\Schema $property, array $groups = null
false,
$types[0]->getClassName(),
$types[0]->isCollection(),
- $types[0]->getCollectionKeyType(),
- // BC layer for symfony < 5.3
- method_exists($types[0], 'getCollectionValueTypes') ?
- ($types[0]->getCollectionValueTypes()[0] ?? null) :
- $types[0]->getCollectionValueType()
+ $types[0]->getCollectionKeyTypes(),
+ $types[0]->getCollectionValueTypes()[0] ?? null
); // ignore nullable field
if ($types[0]->isNullable()) {
diff --git a/Render/Html/GetNelmioAsset.php b/Render/Html/GetNelmioAsset.php
index be642830c..dd4c220ba 100644
--- a/Render/Html/GetNelmioAsset.php
+++ b/Render/Html/GetNelmioAsset.php
@@ -20,18 +20,16 @@
*/
class GetNelmioAsset extends AbstractExtension
{
- private $assetExtension;
- private $resourcesDir;
- private $cdnUrl;
+ private string $resourcesDir;
+ private string $cdnUrl;
- public function __construct(AssetExtension $assetExtension)
+ public function __construct(private AssetExtension $assetExtension)
{
- $this->assetExtension = $assetExtension;
$this->cdnUrl = 'https://cdn.jsdelivr.net/gh/nelmio/NelmioApiDocBundle/Resources/public';
$this->resourcesDir = __DIR__.'/../../Resources/public';
}
- public function getFunctions()
+ public function getFunctions(): array
{
return [
new TwigFunction('nelmioAsset', $this, ['is_safe' => ['html']]),
@@ -51,7 +49,7 @@ public function __invoke($defaultAssetsMode, $asset)
}
}
- private function getExtension($assetsMode, $asset)
+ private function getExtension($assetsMode, $asset): array
{
$extension = mb_substr($asset, -3, 3, 'utf-8');
if ('.js' === $extension) {
@@ -63,7 +61,7 @@ private function getExtension($assetsMode, $asset)
}
}
- private function getResource($asset, $mode)
+ private function getResource($asset, $mode): array
{
if (filter_var($asset, FILTER_VALIDATE_URL)) {
return [$asset, false];
@@ -76,7 +74,7 @@ private function getResource($asset, $mode)
}
}
- private function renderJavascript(string $script, bool $isInline)
+ private function renderJavascript(string $script, bool $isInline): string
{
if ($isInline) {
return sprintf('', $script);
@@ -85,7 +83,7 @@ private function renderJavascript(string $script, bool $isInline)
}
}
- private function renderCss(string $stylesheet, bool $isInline)
+ private function renderCss(string $stylesheet, bool $isInline): string
{
if ($isInline) {
return sprintf('', $stylesheet);
diff --git a/Render/Html/HtmlOpenApiRenderer.php b/Render/Html/HtmlOpenApiRenderer.php
index 39d844cd7..3b380aa91 100644
--- a/Render/Html/HtmlOpenApiRenderer.php
+++ b/Render/Html/HtmlOpenApiRenderer.php
@@ -16,22 +16,21 @@
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
use Twig\Environment;
+use Twig\Error\LoaderError;
+use Twig\Error\RuntimeError;
+use Twig\Error\SyntaxError;
/**
* @internal
*/
class HtmlOpenApiRenderer implements OpenApiRenderer
{
- /** @var Environment|\Twig_Environment */
- private $twig;
-
- /** @var GetNelmioAsset */
- private $getNelmioAsset;
+ private \Twig_Environment|Environment $twig;
public function __construct($twig)
{
if (!$twig instanceof \Twig_Environment && !$twig instanceof Environment) {
- throw new InvalidArgumentException(sprintf('Providing an instance of "%s" as twig is not supported.', get_class($twig)));
+ throw new InvalidArgumentException(sprintf('Providing an instance of "%s" as twig is not supported.', $twig::class));
}
$this->twig = $twig;
}
@@ -41,6 +40,11 @@ public function getFormat(): string
return RenderOpenApi::HTML;
}
+ /**
+ * @throws RuntimeError
+ * @throws SyntaxError
+ * @throws LoaderError
+ */
public function render(OpenApi $spec, array $options = []): string
{
$options += [
diff --git a/Render/RenderOpenApi.php b/Render/RenderOpenApi.php
index c9b759469..49eeac9e5 100644
--- a/Render/RenderOpenApi.php
+++ b/Render/RenderOpenApi.php
@@ -23,15 +23,11 @@ class RenderOpenApi
public const JSON = 'json';
public const YAML = 'yaml';
- /** @var ContainerInterface */
- private $generatorLocator;
-
/** @var array */
- private $openApiRenderers = [];
+ private array $openApiRenderers = [];
- public function __construct(ContainerInterface $generatorLocator, ?OpenApiRenderer ...$openApiRenderers)
+ public function __construct(private ContainerInterface $generatorLocator, ?OpenApiRenderer ...$openApiRenderers)
{
- $this->generatorLocator = $generatorLocator;
foreach ($openApiRenderers as $openApiRenderer) {
if (null === $openApiRenderer) {
continue;
@@ -46,7 +42,7 @@ public function getAvailableFormats(): array
return array_keys($this->openApiRenderers);
}
- public function renderFromRequest(Request $request, string $format, $area, array $extraOptions = [])
+ public function renderFromRequest(Request $request, string $format, $area, array $extraOptions = []): string
{
$options = [];
if ('' !== $request->getBaseUrl()) {
diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst
index 9daa3ea0d..95e294485 100644
--- a/Resources/doc/index.rst
+++ b/Resources/doc/index.rst
@@ -39,7 +39,7 @@ Open a command console, enter your project directory and execute the following c
class AppKernel extends Kernel
{
- public function registerBundles()
+ public function registerBundles(): iterable
{
$bundles = [
// ...
diff --git a/Resources/views/SwaggerUi/index.html.twig b/Resources/views/SwaggerUi/index.html.twig
index a711c2571..fac6ea3e7 100644
--- a/Resources/views/SwaggerUi/index.html.twig
+++ b/Resources/views/SwaggerUi/index.html.twig
@@ -51,13 +51,16 @@ file that was distributed with this source code. #}
{% endblock svg_icons %}
-
- {% block header %}
-
-
-
- {% endblock header %}
-
+
+ {% block header_block %}
+
+ {% block header %}
+
+
+
+ {% endblock header %}
+
+ {% endblock header_block %}
{% block swagger_ui %}
diff --git a/RouteDescriber/FosRestDescriber.php b/RouteDescriber/FosRestDescriber.php
index e3f9f4a4c..458db62d5 100644
--- a/RouteDescriber/FosRestDescriber.php
+++ b/RouteDescriber/FosRestDescriber.php
@@ -11,11 +11,13 @@
namespace Nelmio\ApiDocBundle\RouteDescriber;
+use DateTimeInterface;
use Doctrine\Common\Annotations\Reader;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Controller\Annotations\RequestParam;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\Routing\Route;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\DateTime;
@@ -25,16 +27,8 @@ final class FosRestDescriber implements RouteDescriberInterface
{
use RouteDescriberTrait;
- /** @var Reader */
- private $annotationReader;
-
- /** @var string[] */
- private $mediaTypes;
-
- public function __construct(Reader $annotationReader, array $mediaTypes)
+ public function __construct(private Reader $annotationReader, private array $mediaTypes)
{
- $this->annotationReader = $annotationReader;
- $this->mediaTypes = $mediaTypes;
}
public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $reflectionMethod)
@@ -55,7 +49,7 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
$parameter->required = !$annotation->nullable && $annotation->strict;
- if (OA\UNDEFINED === $parameter->description) {
+ if (Generator::UNDEFINED === $parameter->description) {
$parameter->description = $annotation->description;
}
@@ -63,6 +57,7 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
$parameter->explode = true;
}
+ /** @var OA\Schema $schema */
$schema = Util::getChild($parameter, OA\Schema::class);
$this->describeCommonSchemaFromAnnotation($schema, $annotation);
} else {
@@ -85,7 +80,7 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
}
}
- private function getPattern($requirements)
+ private function getPattern($requirements): ?string
{
if (is_array($requirements) && isset($requirements['rule'])) {
return (string) $requirements['rule'];
@@ -102,12 +97,12 @@ private function getPattern($requirements)
return null;
}
- private function getFormat($requirements)
+ private function getFormat($requirements): ?string
{
if ($requirements instanceof Constraint && !$requirements instanceof Regex) {
if ($requirements instanceof DateTime) {
// As defined per RFC3339
- if (\DateTime::RFC3339 === $requirements->format || 'c' === $requirements->format) {
+ if (DateTimeInterface::RFC3339 === $requirements->format || 'c' === $requirements->format) {
return 'date-time';
}
@@ -128,19 +123,12 @@ private function getFormat($requirements)
private function getContentSchemaForType(OA\RequestBody $requestBody, string $type): OA\Schema
{
- $requestBody->content = OA\UNDEFINED !== $requestBody->content ? $requestBody->content : [];
- switch ($type) {
- case 'json':
- $contentType = 'application/json';
-
- break;
- case 'xml':
- $contentType = 'application/xml';
-
- break;
- default:
- throw new \InvalidArgumentException('Unsupported media type');
- }
+ $requestBody->content = Generator::UNDEFINED !== $requestBody->content ? $requestBody->content : [];
+ $contentType = match ($type) {
+ 'json' => 'application/json',
+ 'xml' => 'application/xml',
+ default => throw new \InvalidArgumentException('Unsupported media type'),
+ };
if (!isset($requestBody->content[$contentType])) {
$requestBody->content[$contentType] = new OA\MediaType(
[
@@ -165,7 +153,7 @@ private function describeCommonSchemaFromAnnotation(OA\Schema $schema, $annotati
{
$schema->default = $annotation->getDefault();
- if (OA\UNDEFINED === $schema->type) {
+ if (Generator::UNDEFINED === $schema->type) {
$schema->type = $annotation->map ? 'array' : 'string';
}
diff --git a/RouteDescriber/PhpDocDescriber.php b/RouteDescriber/PhpDocDescriber.php
index 1ae2ef712..ed1c4c730 100644
--- a/RouteDescriber/PhpDocDescriber.php
+++ b/RouteDescriber/PhpDocDescriber.php
@@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\RouteDescriber;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlockFactoryInterface;
use Symfony\Component\Routing\Route;
@@ -20,7 +21,7 @@ final class PhpDocDescriber implements RouteDescriberInterface
{
use RouteDescriberTrait;
- private $docBlockFactory;
+ private DocBlockFactory|null|DocBlockFactoryInterface $docBlockFactory;
public function __construct(DocBlockFactoryInterface $docBlockFactory = null)
{
@@ -37,20 +38,20 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
try {
$classDocBlock = $this->docBlockFactory->create($reflectionMethod->getDeclaringClass());
- } catch (\Exception $e) {
+ } catch (\Exception) {
}
try {
$docBlock = $this->docBlockFactory->create($reflectionMethod);
- } catch (\Exception $e) {
+ } catch (\Exception) {
}
foreach ($this->getOperations($api, $route) as $operation) {
if (null !== $docBlock) {
- if (OA\UNDEFINED === $operation->summary && '' !== $docBlock->getSummary()) {
+ if (Generator::UNDEFINED === $operation->summary && '' !== $docBlock->getSummary()) {
$operation->summary = $docBlock->getSummary();
}
- if (OA\UNDEFINED === $operation->description && '' !== (string) $docBlock->getDescription()) {
+ if (Generator::UNDEFINED === $operation->description && '' !== (string) $docBlock->getDescription()) {
$operation->description = (string) $docBlock->getDescription();
}
if ($docBlock->hasTag('deprecated')) {
diff --git a/RouteDescriber/RouteDescriberTrait.php b/RouteDescriber/RouteDescriberTrait.php
index 7788a5e45..b089c8913 100644
--- a/RouteDescriber/RouteDescriberTrait.php
+++ b/RouteDescriber/RouteDescriberTrait.php
@@ -42,7 +42,7 @@ private function getOperations(OpenApi $api, Route $route): array
private function normalizePath(string $path): string
{
- if ('.{_format}' === substr($path, -10)) {
+ if (str_ends_with($path, '.{_format}')) {
$path = substr($path, 0, -10);
}
diff --git a/RouteDescriber/RouteMetadataDescriber.php b/RouteDescriber/RouteMetadataDescriber.php
index f68daddb3..eb0796985 100644
--- a/RouteDescriber/RouteMetadataDescriber.php
+++ b/RouteDescriber/RouteMetadataDescriber.php
@@ -14,6 +14,7 @@
use LogicException;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\Routing\Route;
/**
@@ -40,7 +41,7 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
/** @var OA\Parameter $parameter */
$parameter = $existingParams[$paramId] ?? null;
if (null !== $parameter) {
- if (!$parameter->required || OA\UNDEFINED === $parameter->required) {
+ if (!$parameter->required || Generator::UNDEFINED === $parameter->required) {
throw new LogicException(\sprintf('Global parameter "%s" is used as part of route "%s" and must be set as "required"', $pathVariable, $route->getPath()));
}
@@ -52,11 +53,11 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
$parameter->schema = Util::getChild($parameter, OA\Schema::class);
- if (OA\UNDEFINED === $parameter->schema->type) {
+ if (Generator::UNDEFINED === $parameter->schema->type) {
$parameter->schema->type = 'string';
}
- if (isset($requirements[$pathVariable]) && OA\UNDEFINED === $parameter->schema->pattern) {
+ if (isset($requirements[$pathVariable]) && Generator::UNDEFINED === $parameter->schema->pattern) {
$parameter->schema->pattern = $requirements[$pathVariable];
}
}
@@ -71,15 +72,15 @@ public function describe(OA\OpenApi $api, Route $route, \ReflectionMethod $refle
private function getRefParams(OA\OpenApi $api, OA\Operation $operation): array
{
/** @var OA\Parameter[] $globalParams */
- $globalParams = OA\UNDEFINED !== $api->components && OA\UNDEFINED !== $api->components->parameters ? $api->components->parameters : [];
+ $globalParams = Generator::UNDEFINED !== $api->components && Generator::UNDEFINED !== $api->components->parameters ? $api->components->parameters : [];
$globalParams = array_column($globalParams, null, 'parameter'); // update the indexes of the array with the reference names actually used
$existingParams = [];
- $operationParameters = OA\UNDEFINED !== $operation->parameters ? $operation->parameters : [];
+ $operationParameters = Generator::UNDEFINED !== $operation->parameters ? $operation->parameters : [];
/** @var OA\Parameter $parameter */
- foreach ($operationParameters as $id => $parameter) {
+ foreach ($operationParameters as $parameter) {
$ref = $parameter->ref;
- if (OA\UNDEFINED === $ref) {
+ if (Generator::UNDEFINED === $ref) {
// we only concern ourselves with '$ref' parameters, so continue the loop
continue;
}
diff --git a/Routing/FilteredRouteCollectionBuilder.php b/Routing/FilteredRouteCollectionBuilder.php
index 4c9b14b70..b73ee944b 100644
--- a/Routing/FilteredRouteCollectionBuilder.php
+++ b/Routing/FilteredRouteCollectionBuilder.php
@@ -20,23 +20,11 @@
final class FilteredRouteCollectionBuilder
{
- /** @var Reader */
- private $annotationReader;
-
- /** @var ControllerReflector */
- private $controllerReflector;
-
- /** @var string */
- private $area;
-
- /** @var array */
- private $options;
-
public function __construct(
- Reader $annotationReader,
- ControllerReflector $controllerReflector,
- string $area,
- array $options = []
+ private Reader $annotationReader,
+ private ControllerReflector $controllerReflector,
+ private string $area,
+ private array $options = []
) {
$resolver = new OptionsResolver();
$resolver
@@ -60,10 +48,6 @@ public function __construct(
$normalizedOptions = ['path_patterns' => $options];
$options = $normalizedOptions;
}
-
- $this->annotationReader = $annotationReader;
- $this->controllerReflector = $controllerReflector;
- $this->area = $area;
$this->options = $resolver->resolve($options);
}
@@ -159,8 +143,8 @@ private function defaultRouteDisabled(Route $route): bool
$annotations = $this->annotationReader->getMethodAnnotations($method);
foreach ($annotations as $annotation) {
- if (false !== strpos(get_class($annotation), 'Nelmio\\ApiDocBundle\\Annotation')
- || false !== strpos(get_class($annotation), 'OpenApi\\Annotations')
+ if (str_contains($annotation::class, 'Nelmio\\ApiDocBundle\\Annotation')
+ || str_contains($annotation::class, 'OpenApi\\Annotations')
) {
return true;
}
diff --git a/Tests/ApiDocGeneratorTest.php b/Tests/ApiDocGeneratorTest.php
index ec51d4ca0..1642f7365 100644
--- a/Tests/ApiDocGeneratorTest.php
+++ b/Tests/ApiDocGeneratorTest.php
@@ -14,11 +14,15 @@
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Nelmio\ApiDocBundle\Describer\DefaultDescriber;
use PHPUnit\Framework\TestCase;
+use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
class ApiDocGeneratorTest extends TestCase
{
- public function testCache()
+ /**
+ * @throws InvalidArgumentException
+ */
+ public function testCache(): void
{
$adapter = new ArrayAdapter();
$generator = new ApiDocGenerator([new DefaultDescriber()], [], $adapter);
@@ -26,7 +30,10 @@ public function testCache()
$this->assertEquals(json_encode($generator->generate()), json_encode($adapter->getItem('openapi_doc')->get()));
}
- public function testCacheWithCustomId()
+ /**
+ * @throws InvalidArgumentException
+ */
+ public function testCacheWithCustomId(): void
{
$adapter = new ArrayAdapter();
$generator = new ApiDocGenerator([new DefaultDescriber()], [], $adapter, 'custom_id');
diff --git a/Tests/Command/DumpCommandTest.php b/Tests/Command/DumpCommandTest.php
index 9d82f386b..28a0a41b9 100644
--- a/Tests/Command/DumpCommandTest.php
+++ b/Tests/Command/DumpCommandTest.php
@@ -30,7 +30,7 @@ public function testJson(array $jsonOptions, int $expectedJsonFlags)
);
}
- public function provideJsonMode()
+ public function provideJsonMode(): array
{
return [
'pretty print' => [[], JSON_PRETTY_PRINT],
@@ -64,7 +64,7 @@ public function testHtml($htmlConfig, string $expectedHtml)
self::assertStringContainsString($expectedHtml, $output);
}
- public function provideAssetsMode()
+ public function provideAssetsMode(): array
{
return [
'default mode is cdn' => [
@@ -100,7 +100,7 @@ public function provideAssetsMode()
];
}
- private function executeDumpCommand(array $options)
+ private function executeDumpCommand(array $options): string
{
$kernel = static::bootKernel();
$application = new Application($kernel);
diff --git a/Tests/Describer/AbstractDescriberTest.php b/Tests/Describer/AbstractDescriberTest.php
index 87090424f..0583a2e54 100644
--- a/Tests/Describer/AbstractDescriberTest.php
+++ b/Tests/Describer/AbstractDescriberTest.php
@@ -17,8 +17,7 @@
abstract class AbstractDescriberTest extends TestCase
{
- /** @var DescriberInterface */
- protected $describer;
+ protected DescriberInterface $describer;
protected function getOpenApiDoc(): OpenApi
{
diff --git a/Tests/Describer/ApiPlatformDescriberTest.php b/Tests/Describer/ApiPlatformDescriberTest.php
index 13fb1a552..5857351ad 100644
--- a/Tests/Describer/ApiPlatformDescriberTest.php
+++ b/Tests/Describer/ApiPlatformDescriberTest.php
@@ -19,9 +19,8 @@
class ApiPlatformDescriberTest extends AbstractDescriberTest
{
- private $documentation;
-
- private $normalizer;
+ private Documentation $documentation;
+ private NormalizerInterface $normalizer;
public function testDescribe()
{
diff --git a/Tests/Describer/RouteDescriberTest.php b/Tests/Describer/RouteDescriberTest.php
index 1d0c9ca32..c39c4b9d0 100644
--- a/Tests/Describer/RouteDescriberTest.php
+++ b/Tests/Describer/RouteDescriberTest.php
@@ -22,9 +22,8 @@
class RouteDescriberTest extends AbstractDescriberTest
{
- private $routes;
-
- private $routeDescriber;
+ private RouteCollection $routes;
+ private RouteDescriberInterface $routeDescriber;
public function testIgnoreWhenNoController()
{
diff --git a/Tests/Functional/ArrayItemsErrorTest.php b/Tests/Functional/ArrayItemsErrorTest.php
index 57a0ea01b..cfeaa6d4d 100644
--- a/Tests/Functional/ArrayItemsErrorTest.php
+++ b/Tests/Functional/ArrayItemsErrorTest.php
@@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
use Nelmio\ApiDocBundle\Exception\UndocumentedArrayItemsException;
+use Symfony\Component\HttpKernel\KernelInterface;
class ArrayItemsErrorTest extends WebTestCase
{
@@ -30,7 +31,7 @@ public function testModelPictureDocumentation()
$this->getOpenApiDefinition();
}
- protected static function createKernel(array $options = [])
+ protected static function createKernel(array $options = []): KernelInterface
{
return new TestKernel(TestKernel::ERROR_ARRAY_ITEMS);
}
diff --git a/Tests/Functional/BazingaFunctionalTest.php b/Tests/Functional/BazingaFunctionalTest.php
index ff46e8dd9..eaa6f7804 100644
--- a/Tests/Functional/BazingaFunctionalTest.php
+++ b/Tests/Functional/BazingaFunctionalTest.php
@@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
use Hateoas\Configuration\Embedded;
+use Symfony\Component\HttpKernel\KernelInterface;
class BazingaFunctionalTest extends WebTestCase
{
@@ -98,7 +99,7 @@ public function testWithType()
{
try {
new \ReflectionMethod(Embedded::class, 'getType');
- } catch (\ReflectionException $e) {
+ } catch (\ReflectionException) {
$this->markTestSkipped('Typed embedded properties require at least willdurand/hateoas 3.0');
}
$this->assertEquals([
@@ -123,7 +124,7 @@ public function testWithType()
], json_decode($this->getModel('BazingaUserTyped')->toJson(), true));
}
- protected static function createKernel(array $options = [])
+ protected static function createKernel(array $options = []): KernelInterface
{
return new TestKernel(TestKernel::USE_JMS | TestKernel::USE_BAZINGA);
}
diff --git a/Tests/Functional/Controller/ApiController.php b/Tests/Functional/Controller/ApiController.php
index 69372d372..e579979f6 100644
--- a/Tests/Functional/Controller/ApiController.php
+++ b/Tests/Functional/Controller/ApiController.php
@@ -25,9 +25,7 @@
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route("/api", name="api_", host="api.example.com")
- */
+#[Route(path: '/api', name: 'api_', host: 'api.example.com')]
class ApiController
{
/**
@@ -50,8 +48,6 @@ public function fetchArticleAction()
/**
* The method LINK is not supported by OpenAPI so the method will be ignored.
*
- * @Route("/swagger", methods={"GET", "LINK"})
- * @Route("/swagger2", methods={"GET"})
* @Operation(
* @OA\Response(response="201", description="An example resource")
* )
@@ -64,12 +60,13 @@ public function fetchArticleAction()
* @OA\Response(response="203", description="but 203 is not actually allowed (wrong method)")
* )
*/
+ #[Route(path: '/swagger', methods: ['GET', 'LINK'])]
+ #[Route(path: '/swagger2', methods: ['GET'])]
public function swaggerAction()
{
}
/**
- * @Route("/swagger/implicit", methods={"GET", "POST"})
* @OA\Response(
* response="201",
* description="Operation automatically detected",
@@ -84,12 +81,12 @@ public function swaggerAction()
* )
* @OA\Tag(name="implicit")
*/
+ #[Route(path: '/swagger/implicit', methods: ['GET', 'POST'])]
public function implicitSwaggerAction()
{
}
/**
- * @Route("/test/users/{user}", methods={"POST"}, schemes={"https"}, requirements={"user"="/foo/"})
* @OA\Response(
* response="201",
* description="Operation automatically detected",
@@ -100,14 +97,15 @@ public function implicitSwaggerAction()
* @Model(type=UserType::class, options={"bar": "baz"}))
* )
*/
+ #[Route(path: '/test/users/{user}', methods: ['POST'], schemes: ['https'], requirements: ['user' => '/foo/'])]
public function submitUserTypeAction()
{
}
/**
- * @Route("/test/{user}", methods={"GET"}, schemes={"https"}, requirements={"user"="/foo/"})
* @OA\Response(response=200, description="sucessful")
*/
+ #[Route(path: '/test/{user}', methods: ['GET'], schemes: ['https'], requirements: ['user' => '/foo/'])]
public function userAction()
{
}
@@ -117,19 +115,17 @@ public function userAction()
*
* Please do not use this action.
*
- * @Route("/deprecated", methods={"GET"})
- *
* @deprecated
*/
+ #[Route(path: '/deprecated', methods: ['GET'])]
public function deprecatedAction()
{
}
/**
* This action is not documented. It is excluded by the config.
- *
- * @Route("/admin", methods={"GET"})
*/
+ #[Route(path: '/admin', methods: ['GET'])]
public function adminAction()
{
}
@@ -145,36 +141,36 @@ public function filteredAction()
}
/**
- * @Route("/form", methods={"POST"})
* @OA\RequestBody(
* description="Request content",
* @Model(type=DummyType::class))
* )
* @OA\Response(response="201", description="")
*/
+ #[Route(path: '/form', methods: ['POST'])]
public function formAction()
{
}
/**
- * @Route("/security")
* @OA\Response(response="201", description="")
* @Security(name="api_key")
* @Security(name="basic")
* @Security(name="oauth2", scopes={"scope_1"})
*/
+ #[Route(path: '/security')]
public function securityAction()
{
}
/**
- * @Route("/swagger/symfonyConstraints", methods={"GET"})
* @OA\Response(
* response="201",
* description="Used for symfony constraints test",
* @Model(type=SymfonyConstraints::class)
* )
*/
+ #[Route(path: '/swagger/symfonyConstraints', methods: ['GET'])]
public function symfonyConstraintsAction()
{
}
@@ -189,65 +185,59 @@ public function symfonyConstraintsAction()
* response="201",
* ref="#/components/responses/201"
* )
- * @Route("/configReference", methods={"GET"})
*/
+ #[Route(path: '/configReference', methods: ['GET'])]
public function configReferenceAction()
{
}
/**
- * @Route("/multi-annotations", methods={"GET", "POST"})
* @OA\Get(description="This is the get operation")
* @OA\Post(description="This is post")
- *
* @OA\Response(response=200, description="Worked well!", @Model(type=DummyType::class))
*/
+ #[Route(path: '/multi-annotations', methods: ['GET', 'POST'])]
public function operationsWithOtherAnnotations()
{
}
/**
- * @Route("/areas/new", methods={"GET", "POST"})
- *
* @Areas({"area", "area2"})
*/
+ #[Route(path: '/areas/new', methods: ['GET', 'POST'])]
public function newAreaAction()
{
}
/**
- * @Route("/compound", methods={"GET", "POST"})
- *
* @OA\Response(response=200, description="Worked well!", @Model(type=CompoundEntity::class))
*/
+ #[Route(path: '/compound', methods: ['GET', 'POST'])]
public function compoundEntityAction()
{
}
/**
- * @Route("/discriminator-mapping", methods={"GET", "POST"})
- *
* @OA\Response(response=200, description="Worked well!", @Model(type=SymfonyDiscriminator::class))
*/
+ #[Route(path: '/discriminator-mapping', methods: ['GET', 'POST'])]
public function discriminatorMappingAction()
{
}
/**
- * @Route("/named_route-operation-id", name="named_route_operation_id", methods={"GET", "POST"})
- *
* @OA\Response(response=200, description="success")
*/
+ #[Route(path: '/named_route-operation-id', name: 'named_route_operation_id', methods: ['GET', 'POST'])]
public function namedRouteOperationIdAction()
{
}
/**
- * @Route("/custom-operation-id", methods={"GET", "POST"})
- *
* @Operation(operationId="custom-operation-id")
* @OA\Response(response=200, description="success")
*/
+ #[Route(path: '/custom-operation-id', methods: ['GET', 'POST'])]
public function customOperationIdAction()
{
}
diff --git a/Tests/Functional/Controller/ArrayItemsErrorController.php b/Tests/Functional/Controller/ArrayItemsErrorController.php
index 00c5751d3..a9895e752 100644
--- a/Tests/Functional/Controller/ArrayItemsErrorController.php
+++ b/Tests/Functional/Controller/ArrayItemsErrorController.php
@@ -16,19 +16,17 @@
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route(host="api.example.com")
- */
+#[Route(host: 'api.example.com')]
class ArrayItemsErrorController
{
/**
- * @Route("/api/error", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=Foo::class)
* )
*/
+ #[Route(path: '/api/error', methods: ['GET'])]
public function errorAction()
{
}
diff --git a/Tests/Functional/Controller/BazingaController.php b/Tests/Functional/Controller/BazingaController.php
index f8bb7d670..fcbe8fd07 100644
--- a/Tests/Functional/Controller/BazingaController.php
+++ b/Tests/Functional/Controller/BazingaController.php
@@ -16,31 +16,29 @@
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route(host="api.example.com")
- */
+#[Route(host: 'api.example.com')]
class BazingaController
{
/**
- * @Route("/api/bazinga", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=BazingaUser::class)
* )
*/
+ #[Route(path: '/api/bazinga', methods: ['GET'])]
public function userAction()
{
}
/**
- * @Route("/api/bazinga_foo", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=BazingaUser::class, groups={"foo"})
* )
*/
+ #[Route(path: '/api/bazinga_foo', methods: ['GET'])]
public function userGroupAction()
{
}
diff --git a/Tests/Functional/Controller/BazingaTypedController.php b/Tests/Functional/Controller/BazingaTypedController.php
index ad72687f3..271ebe104 100644
--- a/Tests/Functional/Controller/BazingaTypedController.php
+++ b/Tests/Functional/Controller/BazingaTypedController.php
@@ -16,19 +16,17 @@
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route(host="api.example.com")
- */
+#[Route(host: 'api.example.com')]
class BazingaTypedController
{
/**
- * @Route("/api/bazinga_typed", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=BazingaUserTyped::class)
* )
*/
+ #[Route(path: '/api/bazinga_typed', methods: ['GET'])]
public function userTypedAction()
{
}
diff --git a/Tests/Functional/Controller/ClassApiController.php b/Tests/Functional/Controller/ClassApiController.php
index 21bbd0e45..8687da2fb 100644
--- a/Tests/Functional/Controller/ClassApiController.php
+++ b/Tests/Functional/Controller/ClassApiController.php
@@ -16,15 +16,15 @@
use Symfony\Component\Routing\Annotation\Route;
/**
- * @Route("/api", host="api.example.com")
* @Security(name="basic")
*/
+#[Route(path: '/api', host: 'api.example.com')]
class ClassApiController
{
/**
- * @Route("/security/class")
* @OA\Response(response="201", description="")
*/
+ #[Route(path: '/security/class')]
public function securityAction()
{
}
diff --git a/Tests/Functional/Controller/FOSRestController.php b/Tests/Functional/Controller/FOSRestController.php
index 772af464d..cb3538a39 100644
--- a/Tests/Functional/Controller/FOSRestController.php
+++ b/Tests/Functional/Controller/FOSRestController.php
@@ -18,13 +18,10 @@
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Regex;
-/**
- * @Route("/api", host="api.example.com")
- */
+#[Route(path: '/api', host: 'api.example.com')]
class FOSRestController
{
/**
- * @Route("/fosrest.{_format}", methods={"POST"})
* @QueryParam(name="foo", requirements=@Regex("/^\d+$/"))
* @QueryParam(name="mapped", map=true)
* @RequestParam(name="Barraa", key="bar", requirements="\d+")
@@ -34,6 +31,7 @@ class FOSRestController
* @RequestParam(name="datetimeNoFormat", requirements=@DateTime())
* @RequestParam(name="date", requirements=@DateTime("Y-m-d"))
*/
+ #[Route(path: '/fosrest.{_format}', methods: ['POST'])]
public function fosrestAction()
{
}
diff --git a/Tests/Functional/Controller/InvokableController.php b/Tests/Functional/Controller/InvokableController.php
index b640b70be..8f78f946e 100644
--- a/Tests/Functional/Controller/InvokableController.php
+++ b/Tests/Functional/Controller/InvokableController.php
@@ -17,12 +17,12 @@
/**
* Prevents a regression (see https://github.com/nelmio/NelmioApiDocBundle/issues/1559).
*
- * @Route("/api/invoke", host="api.example.com", name="invokable", methods={"GET"})
* @OA\Response(
* response=200,
* description="Invokable!"
* )
*/
+#[Route(path: '/api/invoke', host: 'api.example.com', name: 'invokable', methods: ['GET'])]
class InvokableController
{
public function __invoke()
diff --git a/Tests/Functional/Controller/JMSController.php b/Tests/Functional/Controller/JMSController.php
index 392eace10..1c68521da 100644
--- a/Tests/Functional/Controller/JMSController.php
+++ b/Tests/Functional/Controller/JMSController.php
@@ -24,115 +24,113 @@
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route(host="api.example.com")
- */
+#[Route(host: 'api.example.com')]
class JMSController
{
/**
- * @Route("/api/jms", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSUser::class)
* )
*/
+ #[Route(path: '/api/jms', methods: ['GET'])]
public function userAction()
{
}
/**
- * @Route("/api/yaml", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=VirtualProperty::class)
* )
*/
+ #[Route(path: '/api/yaml', methods: ['GET'])]
public function yamlAction()
{
}
/**
- * @Route("/api/jms_complex", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSComplex::class, groups={"list", "details", "User" : {"list"}})
* )
*/
+ #[Route(path: '/api/jms_complex', methods: ['GET'])]
public function complexAction()
{
}
/**
- * @Route("/api/jms_complex_dual", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSDualComplex::class, groups={"Default", "complex" : {"User" : {"details"}}})
* )
*/
+ #[Route(path: '/api/jms_complex_dual', methods: ['GET'])]
public function complexDualAction()
{
}
/**
- * @Route("/api/jms_naming_strategy", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSNamingStrategyConstraints::class, groups={"Default"})
* )
*/
+ #[Route(path: '/api/jms_naming_strategy', methods: ['GET'])]
public function namingStrategyConstraintsAction()
{
}
/**
- * @Route("/api/jms_chat", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSChat::class, groups={"Default", "members" : {"mini"}})
* )
*/
+ #[Route(path: '/api/jms_chat', methods: ['GET'])]
public function chatAction()
{
}
/**
- * @Route("/api/jms_picture", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSPicture::class, groups={"mini"})
* )
*/
+ #[Route(path: '/api/jms_picture', methods: ['GET'])]
public function pictureAction()
{
}
/**
- * @Route("/api/jms_mini_user", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSChatUser::class, groups={"mini"})
* )
*/
+ #[Route(path: '/api/jms_mini_user', methods: ['GET'])]
public function minUserAction()
{
}
/**
- * @Route("/api/jms_mini_user_nested", methods={"GET"})
* @OA\Response(
* response=200,
* description="Success",
* @Model(type=JMSChatRoomUser::class, groups={"mini", "friend": {"living":{"Default"}}})
* )
*/
+ #[Route(path: '/api/jms_mini_user_nested', methods: ['GET'])]
public function minUserNestedAction()
{
}
diff --git a/Tests/Functional/Controller/SerializedNameController.php b/Tests/Functional/Controller/SerializedNameController.php
index 736a2669f..18b67f775 100644
--- a/Tests/Functional/Controller/SerializedNameController.php
+++ b/Tests/Functional/Controller/SerializedNameController.php
@@ -18,9 +18,8 @@
/**
* This controller is only loaded when SerializedName exists (sf >= 4.2).
- *
- * @Route("/api", host="api.example.com")
*/
+#[Route(path: '/api', host: 'api.example.com')]
class SerializedNameController
{
/**
@@ -29,8 +28,8 @@ class SerializedNameController
* description="success",
* @Model(type=SerializedNameEnt::class)
* )
- * @Route("/serializename", methods={"GET"})
*/
+ #[Route(path: '/serializename', methods: ['GET'])]
public function serializedNameAction()
{
}
diff --git a/Tests/Functional/Controller/TestController.php b/Tests/Functional/Controller/TestController.php
index 7e887e613..e96045902 100644
--- a/Tests/Functional/Controller/TestController.php
+++ b/Tests/Functional/Controller/TestController.php
@@ -14,9 +14,7 @@
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route("/test", host="api-test.example.com")
- */
+#[Route(path: '/test', host: 'api-test.example.com')]
class TestController
{
/**
@@ -24,8 +22,8 @@ class TestController
* response="200",
* description="Test"
* )
- * @Route("/test/", methods={"GET"})
*/
+ #[Route(path: '/test/', methods: ['GET'])]
public function testAction()
{
}
diff --git a/Tests/Functional/Controller/UndocumentedController.php b/Tests/Functional/Controller/UndocumentedController.php
index 98fbe6562..7c5c35b00 100644
--- a/Tests/Functional/Controller/UndocumentedController.php
+++ b/Tests/Functional/Controller/UndocumentedController.php
@@ -13,16 +13,13 @@
use Symfony\Component\Routing\Annotation\Route;
-/**
- * @Route(host="api.example.com")
- */
+#[Route(host: 'api.example.com')]
class UndocumentedController
{
/**
* This path is excluded by the config (only /api allowed).
- *
- * @Route("/undocumented", methods={"GET"})
*/
+ #[Route(path: '/undocumented', methods: ['GET'])]
public function undocumentedAction()
{
}
diff --git a/Tests/Functional/Entity/Article.php b/Tests/Functional/Entity/Article.php
index 922e54620..fe44c45e5 100644
--- a/Tests/Functional/Entity/Article.php
+++ b/Tests/Functional/Entity/Article.php
@@ -18,9 +18,7 @@
*/
class Article
{
- /**
- * @Groups({"light"})
- */
+ #[Groups(groups: ['light'])]
public function setAuthor(User $author)
{
}
diff --git a/Tests/Functional/Entity/Dummy.php b/Tests/Functional/Entity/Dummy.php
index 1a07b9ade..1987bba15 100644
--- a/Tests/Functional/Entity/Dummy.php
+++ b/Tests/Functional/Entity/Dummy.php
@@ -37,9 +37,9 @@ class Dummy
/**
* @var string
*
- * @Assert\NotBlank
* @ApiProperty(iri="http://schema.org/name")
*/
+ #[Assert\NotBlank]
private $name;
public function getId(): int
diff --git a/Tests/Functional/Entity/JMSNamingStrategyConstraints.php b/Tests/Functional/Entity/JMSNamingStrategyConstraints.php
index 1103e20eb..376579920 100644
--- a/Tests/Functional/Entity/JMSNamingStrategyConstraints.php
+++ b/Tests/Functional/Entity/JMSNamingStrategyConstraints.php
@@ -21,11 +21,10 @@ class JMSNamingStrategyConstraints
*
* @Serializer\Type("string")
* @Serializer\SerializedName("beautifulName")
- *
- * @Assert\NotBlank()
- * @Assert\Regex(pattern="\w+")
- * @Assert\Length(min="3", max="10")
*/
+ #[Assert\NotBlank]
+ #[Assert\Regex(pattern: '\w+')]
+ #[Assert\Length(min: 3, max: 10)]
private $some_weird_named_property = 'default';
public function getSomeWeirdNamedProperty(): string
diff --git a/Tests/Functional/Entity/SymfonyConstraints.php b/Tests/Functional/Entity/SymfonyConstraints.php
index 05e65c952..f47043d45 100644
--- a/Tests/Functional/Entity/SymfonyConstraints.php
+++ b/Tests/Functional/Entity/SymfonyConstraints.php
@@ -18,96 +18,80 @@ class SymfonyConstraints
{
/**
* @var int
- *
- * @Assert\NotBlank()
*/
+ #[Assert\NotBlank]
private $propertyNotBlank;
/**
* @var int
- *
- * @Assert\NotNull()
*/
+ #[Assert\NotNull]
private $propertyNotNull;
/**
* @var int
- *
- * @Assert\Length(min="0", max="50")
*/
+ #[Assert\Length(min: 0, max: 50)]
private $propertyAssertLength;
/**
* @var int
- *
- * @Assert\Regex(pattern="/[a-z]{2}/")
*/
+ #[Assert\Regex(pattern: '/[a-z]{2}/')]
private $propertyRegex;
/**
* @var int
- *
- * @Assert\Count(min="0", max="10")
*/
+ #[Assert\Count(min: 0, max: 10)]
private $propertyCount;
/**
* @var int
- *
- * @Assert\Choice(choices={"choice1", "choice2"})
*/
+ #[Assert\Choice(choices: ['choice1', 'choice2'])]
private $propertyChoice;
/**
* @var int
- *
- * @Assert\Choice(callback={SymfonyConstraints::class,"fetchAllowedChoices"})
*/
+ #[Assert\Choice(callback: [SymfonyConstraints::class, 'fetchAllowedChoices'])]
private $propertyChoiceWithCallback;
/**
* @var int
- *
- * @Assert\Choice(callback="fetchAllowedChoices")
*/
+ #[Assert\Choice(callback: 'fetchAllowedChoices')]
private $propertyChoiceWithCallbackWithoutClass;
/**
* @var string[]
- *
- * @Assert\Choice(multiple=true, choices={"choice1", "choice2"})
*/
+ #[Assert\Choice(multiple: true, choices: ['choice1', 'choice2'])]
private $propertyChoiceWithMultiple;
/**
* @var int
- *
- * @Assert\Expression(
- * "this.getCategory() in ['php', 'symfony'] or !this.isTechnicalPost()",
- * message="If this is a tech post, the category should be either php or symfony!"
- * )
*/
+ #[Assert\Expression(expression: "this.getCategory() in ['php', 'symfony'] or !this.isTechnicalPost()", message: 'If this is a tech post, the category should be either php or symfony!')]
private $propertyExpression;
/**
* @var int
- *
- * @Assert\Range(min=1, max=5)
*/
+ #[Assert\Range(min: 1, max: 5)]
private $propertyRange;
/**
* @var int
- *
- * @Assert\LessThan(42)
*/
+ #[Assert\LessThan(value: 42)]
private $propertyLessThan;
/**
* @var int
- *
- * @Assert\LessThanOrEqual(23)
*/
+ #[Assert\LessThanOrEqual(value: 23)]
private $propertyLessThanOrEqual;
/**
@@ -122,9 +106,7 @@ public function setPropertyWithCompoundValidationRule(int $propertyWithCompoundV
$this->propertyWithCompoundValidationRule = $propertyWithCompoundValidationRule;
}
- /**
- * @Assert\Count(min="0", max="10")
- */
+ #[Assert\Count(min: 0, max: 10)]
public function setPropertyNotBlank(int $propertyNotBlank): void
{
$this->propertyNotBlank = $propertyNotBlank;
diff --git a/Tests/Functional/FOSRestTest.php b/Tests/Functional/FOSRestTest.php
index 66917b829..cd1cfbe11 100644
--- a/Tests/Functional/FOSRestTest.php
+++ b/Tests/Functional/FOSRestTest.php
@@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
class FOSRestTest extends WebTestCase
{
@@ -37,17 +38,17 @@ public function testFOSRestAction()
$fooParameter = $this->getParameter($operation, 'foo', 'query');
$this->assertInstanceOf(OA\Schema::class, $fooParameter->schema);
$this->assertEquals('\d+', $fooParameter->schema->pattern);
- $this->assertEquals(OA\UNDEFINED, $fooParameter->schema->format);
+ $this->assertEquals(Generator::UNDEFINED, $fooParameter->schema->format);
$mappedParameter = $this->getParameter($operation, 'mapped[]', 'query');
$this->assertTrue($mappedParameter->explode);
$barProperty = $this->getProperty($bodySchema, 'bar');
$this->assertEquals('\d+', $barProperty->pattern);
- $this->assertEquals(OA\UNDEFINED, $barProperty->format);
+ $this->assertEquals(Generator::UNDEFINED, $barProperty->format);
$bazProperty = $this->getProperty($bodySchema, 'baz');
- $this->assertEquals(OA\UNDEFINED, $bazProperty->pattern);
+ $this->assertEquals(Generator::UNDEFINED, $bazProperty->pattern);
$this->assertEquals('IsTrue', $bazProperty->format);
$dateTimeProperty = $this->getProperty($bodySchema, 'datetime');
@@ -57,7 +58,7 @@ public function testFOSRestAction()
$this->assertEquals('date-time', $dateTimeAltProperty->format);
$dateTimeNoFormatProperty = $this->getProperty($bodySchema, 'datetimeNoFormat');
- $this->assertEquals(OA\UNDEFINED, $dateTimeNoFormatProperty->format);
+ $this->assertEquals(Generator::UNDEFINED, $dateTimeNoFormatProperty->format);
$dateProperty = $this->getProperty($bodySchema, 'date');
$this->assertEquals('date', $dateProperty->format);
diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php
index 609446633..0704bc6a8 100644
--- a/Tests/Functional/FunctionalTest.php
+++ b/Tests/Functional/FunctionalTest.php
@@ -14,6 +14,7 @@
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use Nelmio\ApiDocBundle\Tests\Helper;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Component\Serializer\Annotation\SerializedName;
class FunctionalTest extends WebTestCase
@@ -84,7 +85,7 @@ public function swaggerActionPathsProvider()
public function testAnnotationWithManualPath()
{
$path = $this->getPath('/api/swagger2');
- $this->assertSame(OA\UNDEFINED, $path->post);
+ $this->assertSame(Generator::UNDEFINED, $path->post);
$operation = $this->getOperation('/api/swagger', 'get');
$this->assertNotHasParameter('Accept-Version', 'header', $operation);
@@ -123,10 +124,10 @@ public function testUserAction()
{
$operation = $this->getOperation('/api/test/{user}', 'get');
- $this->assertEquals(OA\UNDEFINED, $operation->security);
- $this->assertEquals(OA\UNDEFINED, $operation->summary);
- $this->assertEquals(OA\UNDEFINED, $operation->description);
- $this->assertEquals(OA\UNDEFINED, $operation->deprecated);
+ $this->assertEquals(Generator::UNDEFINED, $operation->security);
+ $this->assertEquals(Generator::UNDEFINED, $operation->summary);
+ $this->assertEquals(Generator::UNDEFINED, $operation->description);
+ $this->assertEquals(Generator::UNDEFINED, $operation->deprecated);
$this->assertHasResponse(200, $operation);
$this->assertHasParameter('user', 'path', $operation);
@@ -134,7 +135,7 @@ public function testUserAction()
$this->assertTrue($parameter->required);
$this->assertEquals('string', $parameter->schema->type);
$this->assertEquals('/foo/', $parameter->schema->pattern);
- $this->assertEquals(OA\UNDEFINED, $parameter->schema->format);
+ $this->assertEquals(Generator::UNDEFINED, $parameter->schema->format);
}
public function testDeprecatedAction()
@@ -551,7 +552,7 @@ public function testModelsWithDiscriminatorMapAreLoadedWithOpenApiPolymorphism()
$this->assertCount(2, $model->discriminator->mapping);
$this->assertArrayHasKey('one', $model->discriminator->mapping);
$this->assertArrayHasKey('two', $model->discriminator->mapping);
- $this->assertNotSame(OA\UNDEFINED, $model->oneOf);
+ $this->assertNotSame(Generator::UNDEFINED, $model->oneOf);
$this->assertCount(2, $model->oneOf);
}
diff --git a/Tests/Functional/JMSFunctionalTest.php b/Tests/Functional/JMSFunctionalTest.php
index 221800df1..9bcc3c3b4 100644
--- a/Tests/Functional/JMSFunctionalTest.php
+++ b/Tests/Functional/JMSFunctionalTest.php
@@ -11,6 +11,8 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
+use Symfony\Component\HttpKernel\KernelInterface;
+
class JMSFunctionalTest extends WebTestCase
{
protected function setUp(): void
@@ -333,7 +335,7 @@ public function testNamingStrategyWithConstraints()
], json_decode($this->getModel('JMSNamingStrategyConstraints')->toJson(), true));
}
- protected static function createKernel(array $options = [])
+ protected static function createKernel(array $options = []): KernelInterface
{
return new TestKernel(TestKernel::USE_JMS);
}
diff --git a/Tests/Functional/TestKernel.php b/Tests/Functional/TestKernel.php
index 5de564d2b..eb3622466 100644
--- a/Tests/Functional/TestKernel.php
+++ b/Tests/Functional/TestKernel.php
@@ -53,7 +53,7 @@ public function __construct(int $flags = 0)
/**
* {@inheritdoc}
*/
- public function registerBundles()
+ public function registerBundles(): iterable
{
$bundles = [
new FrameworkBundle(),
@@ -106,7 +106,7 @@ protected function configureRoutes(RouteCollectionBuilder $routes)
try {
new \ReflectionMethod(Embedded::class, 'getType');
$routes->import(__DIR__.'/Controller/BazingaTypedController.php', '/', 'annotation');
- } catch (\ReflectionException $e) {
+ } catch (\ReflectionException) {
}
}
@@ -275,7 +275,7 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load
/**
* {@inheritdoc}
*/
- public function getCacheDir()
+ public function getCacheDir(): string
{
return parent::getCacheDir().'/'.$this->flags;
}
@@ -283,7 +283,7 @@ public function getCacheDir()
/**
* {@inheritdoc}
*/
- public function getLogDir()
+ public function getLogDir(): string
{
return parent::getLogDir().'/'.$this->flags;
}
diff --git a/Tests/Functional/WebTestCase.php b/Tests/Functional/WebTestCase.php
index 2268de2d0..c792c0d2c 100644
--- a/Tests/Functional/WebTestCase.php
+++ b/Tests/Functional/WebTestCase.php
@@ -12,11 +12,13 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
+use Symfony\Component\HttpKernel\KernelInterface;
class WebTestCase extends BaseWebTestCase
{
- protected static function createKernel(array $options = [])
+ protected static function createKernel(array $options = []): KernelInterface
{
return new TestKernel();
}
@@ -93,7 +95,7 @@ protected function getPath($path): OA\PathItem
public function assertHasPath($path, OA\OpenApi $api)
{
- $paths = array_column(OA\UNDEFINED !== $api->paths ? $api->paths : [], 'path');
+ $paths = array_column(Generator::UNDEFINED !== $api->paths ? $api->paths : [], 'path');
static::assertContains(
$path,
$paths,
@@ -103,7 +105,7 @@ public function assertHasPath($path, OA\OpenApi $api)
public function assertNotHasPath($path, OA\OpenApi $api)
{
- $paths = array_column(OA\UNDEFINED !== $api->paths ? $api->paths : [], 'path');
+ $paths = array_column(Generator::UNDEFINED !== $api->paths ? $api->paths : [], 'path');
static::assertNotContains(
$path,
$paths,
@@ -113,7 +115,7 @@ public function assertNotHasPath($path, OA\OpenApi $api)
public function assertHasResponse($responseCode, OA\Operation $operation)
{
- $responses = array_column(OA\UNDEFINED !== $operation->responses ? $operation->responses : [], 'response');
+ $responses = array_column(Generator::UNDEFINED !== $operation->responses ? $operation->responses : [], 'response');
static::assertContains(
$responseCode,
$responses,
@@ -124,7 +126,7 @@ public function assertHasResponse($responseCode, OA\Operation $operation)
public function assertHasParameter($name, $in, OA\AbstractAnnotation $annotation)
{
/* @var OA\Operation|OA\OpenApi $annotation */
- $parameters = array_filter(OA\UNDEFINED !== $annotation->parameters ? $annotation->parameters : [], function (OA\Parameter $parameter) use ($name, $in) {
+ $parameters = array_filter(Generator::UNDEFINED !== $annotation->parameters ? $annotation->parameters : [], function (OA\Parameter $parameter) use ($name, $in) {
return $parameter->name === $name && $parameter->in === $in;
});
@@ -137,7 +139,7 @@ public function assertHasParameter($name, $in, OA\AbstractAnnotation $annotation
public function assertNotHasParameter($name, $in, OA\AbstractAnnotation $annotation)
{
/* @var OA\Operation|OA\OpenApi $annotation */
- $parameters = array_column(OA\UNDEFINED !== $annotation->parameters ? $annotation->parameters : [], 'name', 'in');
+ $parameters = array_column(Generator::UNDEFINED !== $annotation->parameters ? $annotation->parameters : [], 'name', 'in');
static::assertNotContains(
$name,
$parameters[$in] ?? [],
@@ -148,7 +150,7 @@ public function assertNotHasParameter($name, $in, OA\AbstractAnnotation $annotat
public function assertHasProperty($property, OA\AbstractAnnotation $annotation)
{
/* @var OA\Schema|OA\Property|OA\Items $annotation */
- $properties = array_column(OA\UNDEFINED !== $annotation->properties ? $annotation->properties : [], 'property');
+ $properties = array_column(Generator::UNDEFINED !== $annotation->properties ? $annotation->properties : [], 'property');
static::assertContains(
$property,
$properties,
@@ -159,7 +161,7 @@ public function assertHasProperty($property, OA\AbstractAnnotation $annotation)
public function assertNotHasProperty($property, OA\AbstractAnnotation $annotation)
{
/* @var OA\Schema|OA\Property|OA\Items $annotation */
- $properties = array_column(OA\UNDEFINED !== $annotation->properties ? $annotation->properties : [], 'property');
+ $properties = array_column(Generator::UNDEFINED !== $annotation->properties ? $annotation->properties : [], 'property');
static::assertNotContains(
$property,
$properties,
diff --git a/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php b/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php
index 9ee7fc8bc..40e5bd0e3 100644
--- a/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php
+++ b/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php
@@ -16,6 +16,7 @@
use Nelmio\ApiDocBundle\Tests\Helper;
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraints as Assert;
@@ -24,14 +25,10 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
public function testUpdatePropertyFix1283()
{
$entity = new class() {
- /**
- * @Assert\NotBlank()
- * @Assert\Length(min = 1)
- */
+ #[Assert\NotBlank]
+ #[Assert\Length(min: 1)]
private $property1;
- /**
- * @Assert\NotBlank()
- */
+ #[Assert\NotBlank]
private $property2;
};
@@ -76,14 +73,10 @@ public function testOptionalProperty($entity)
public function provideOptionalProperty(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\NotBlank(allowNull = true)
- * @Assert\Length(min = 1)
- */
+ #[Assert\NotBlank(allowNull: true)]
+ #[Assert\Length(min: 1)]
private $property1;
- /**
- * @Assert\NotBlank()
- */
+ #[Assert\NotBlank]
private $property2;
}];
@@ -124,10 +117,8 @@ public function provideAssertChoiceResultsInNumericArray(): iterable
]);
yield 'Annotations' => [new class() {
- /**
- * @Assert\Length(min = 1)
- * @Assert\Choice(choices=TEST_ASSERT_CHOICE_STATUSES)
- */
+ #[Assert\Length(min: 1)]
+ #[Assert\Choice(choices: 'TEST_ASSERT_CHOICE_STATUSES')]
private $property1;
}];
@@ -161,9 +152,7 @@ public function testMultipleChoiceConstraintsApplyEnumToItems($entity)
public function provideMultipleChoiceConstraintsApplyEnumToItems(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Choice(choices={"one", "two"}, multiple=true)
- */
+ #[Assert\Choice(choices: ['one', 'two'], multiple: true)]
private $property1;
}];
@@ -190,16 +179,14 @@ public function testLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet($entity)
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->maxLength);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->maxLength);
$this->assertSame(1, $schema->properties[0]->minLength);
}
public function provideLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Length(min = 1)
- */
+ #[Assert\Length(min: 1)]
private $property1;
}];
@@ -226,16 +213,14 @@ public function testLengthConstraintDoesNotSetMinLengthIfMinIsNotSet($entity)
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->minLength);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->minLength);
$this->assertSame(100, $schema->properties[0]->maxLength);
}
public function provideLengthConstraintDoesNotSetMinLengthIfMinIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Length(max = 100)
- */
+ #[Assert\Length(max: 100)]
private $property1;
}];
@@ -272,11 +257,11 @@ public function testCompoundValidationRules()
$this->assertSame(5, $schema->properties[0]->maximum);
$this->assertTrue($schema->properties[0]->exclusiveMaximum);
} else {
- $this->assertSame(OA\UNDEFINED, $schema->required);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->exclusiveMinimum);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->maximum);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->exclusiveMaximum);
+ $this->assertSame(Generator::UNDEFINED, $schema->required);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->minimum);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->exclusiveMinimum);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->maximum);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->exclusiveMaximum);
}
}
@@ -295,16 +280,14 @@ public function testCountConstraintDoesNotSetMinItemsIfMinIsNotSet($entity)
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->minItems);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->minItems);
$this->assertSame(10, $schema->properties[0]->maxItems);
}
public function provideCountConstraintDoesNotSetMinItemsIfMinIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Count(max = 10)
- */
+ #[Assert\Count(max: 10)]
private $property1;
}];
@@ -331,16 +314,14 @@ public function testCountConstraintDoesNotSetMaxItemsIfMaxIsNotSet($entity)
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->maxItems);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->maxItems);
$this->assertSame(10, $schema->properties[0]->minItems);
}
public function provideCountConstraintDoesNotSetMaxItemsIfMaxIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Count(min = 10)
- */
+ #[Assert\Count(min: 10)]
private $property1;
}];
@@ -367,16 +348,14 @@ public function testRangeConstraintDoesNotSetMaximumIfMaxIsNotSet($entity)
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->maximum);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->maximum);
$this->assertSame(10, $schema->properties[0]->minimum);
}
public function provideRangeConstraintDoesNotSetMaximumIfMaxIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Range(min = 10)
- */
+ #[Assert\Range(min: 10)]
private $property1;
}];
@@ -403,16 +382,14 @@ public function testRangeConstraintDoesNotSetMinimumIfMinIsNotSet($entity)
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
- $this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum);
+ $this->assertSame(Generator::UNDEFINED, $schema->properties[0]->minimum);
$this->assertSame(10, $schema->properties[0]->maximum);
}
public function provideRangeConstraintDoesNotSetMinimumIfMinIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
- /**
- * @Assert\Range(max = 10)
- */
+ #[Assert\Range(max: 10)]
private $property1;
}];
diff --git a/Tests/ModelDescriber/ApplyOpenApiDiscriminatorTraitTest.php b/Tests/ModelDescriber/ApplyOpenApiDiscriminatorTraitTest.php
index 51df5e407..cf65b8af1 100644
--- a/Tests/ModelDescriber/ApplyOpenApiDiscriminatorTraitTest.php
+++ b/Tests/ModelDescriber/ApplyOpenApiDiscriminatorTraitTest.php
@@ -15,6 +15,7 @@
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use Nelmio\ApiDocBundle\ModelDescriber\ApplyOpenApiDiscriminatorTrait;
use OpenApi\Annotations as OA;
+use OpenApi\Generator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Type;
@@ -57,7 +58,7 @@ public function testApplyAddsOneOfFieldToSchema()
'two' => 'SecondType',
]);
- $this->assertNotSame(OA\UNDEFINED, $this->schema->oneOf);
+ $this->assertNotSame(Generator::UNDEFINED, $this->schema->oneOf);
$this->assertCount(2, $this->schema->oneOf);
$this->assertSame(
$this->modelRegistry->register($this->createModel('FirstType')),
diff --git a/Tests/Render/RenderOpenApiTest.php b/Tests/Render/RenderOpenApiTest.php
index d24e4f7cd..464c18fa7 100644
--- a/Tests/Render/RenderOpenApiTest.php
+++ b/Tests/Render/RenderOpenApiTest.php
@@ -65,11 +65,8 @@ private function renderOpenApi(...$openApiRenderer): void
{
$spec = $this->createMock(OpenApi::class);
$generator = new class($spec) {
- private $spec;
-
- public function __construct($spec)
+ public function __construct(private $spec)
{
- $this->spec = $spec;
}
public function generate()
diff --git a/Tests/SwaggerPhp/UtilTest.php b/Tests/SwaggerPhp/UtilTest.php
index 2b3bfc859..8f8bbca37 100644
--- a/Tests/SwaggerPhp/UtilTest.php
+++ b/Tests/SwaggerPhp/UtilTest.php
@@ -14,7 +14,7 @@
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
use OpenApi\Context;
-use const OpenApi\UNDEFINED;
+use OpenApi\Generator;
use PHPUnit\Framework\TestCase;
/**
@@ -104,10 +104,10 @@ public function testCreateChildWithEmptyProperties()
$info = Util::createChild($this->rootAnnotation, OA\Info::class, $properties);
$properties = array_filter(get_object_vars($info), function ($key) {
- return 0 !== strpos($key, '_');
+ return !str_starts_with($key, '_');
}, ARRAY_FILTER_USE_KEY);
- $this->assertEquals([UNDEFINED], array_unique(array_values($properties)));
+ $this->assertEquals([Generator::UNDEFINED], array_unique(array_values($properties)));
$this->assertIsNested($this->rootAnnotation, $info);
$this->assertIsConnectedToRootContext($info);
@@ -220,7 +220,7 @@ public function testSearchIndexedCollectionItem($setup, $asserts)
foreach ($items as $assert) {
$setupCollection = empty($assert['components']) ?
($setup[$collection] ?? []) :
- (OA\UNDEFINED !== $setup['components']->{$collection} ? $setup['components']->{$collection} : []);
+ (Generator::UNDEFINED !== $setup['components']->{$collection} ? $setup['components']->{$collection} : []);
// get the indexing correct within haystack preparation
$properties = array_fill(0, \count($setupCollection), null);
@@ -829,10 +829,10 @@ private function getSetupPropertiesWithoutClass(array $setup)
private function getNonDefaultProperties($object)
{
$objectVars = \get_object_vars($object);
- $classVars = \get_class_vars(\get_class($object));
+ $classVars = \get_class_vars($object::class);
$props = [];
foreach ($objectVars as $key => $value) {
- if ($value !== $classVars[$key] && 0 !== \strpos($key, '_')) {
+ if ($value !== $classVars[$key] && !str_starts_with($key, '_')) {
$props[$key] = $value;
}
}
diff --git a/Tests/Util/ControllerReflectorTest.php b/Tests/Util/ControllerReflectorTest.php
new file mode 100644
index 000000000..83464cd30
--- /dev/null
+++ b/Tests/Util/ControllerReflectorTest.php
@@ -0,0 +1,29 @@
+assertEquals(
+ ReflectionMethod::class,
+ $controllerReflector->getReflectionMethod([BazingaController::class, 'userAction'])::class
+ );
+ $this->assertEquals(
+ ReflectionMethod::class,
+ $controllerReflector->getReflectionMethod(BazingaController::class.'::userAction')::class
+ );
+ $this->assertNull(
+ $controllerReflector->getReflectionMethod('UnknownController::userAction')
+ );
+ $this->assertNull($controllerReflector->getReflectionMethod(null));
+ }
+}
diff --git a/Util/ControllerReflector.php b/Util/ControllerReflector.php
index 49fe9d7ad..4ee49e106 100644
--- a/Util/ControllerReflector.php
+++ b/Util/ControllerReflector.php
@@ -20,16 +20,11 @@
*/
class ControllerReflector
{
- private $container;
+ private mixed $controllerNameParser;
+ private array $controllers = [];
- private $controllerNameParser;
-
- private $controllers = [];
-
- public function __construct(ContainerInterface $container)
+ public function __construct(private ContainerInterface $container)
{
- $this->container = $container;
-
if (1 < \func_num_args() && func_get_arg(1) instanceof ControllerNameParser) {
$this->controllerNameParser = func_get_arg(1);
}
@@ -37,29 +32,25 @@ public function __construct(ContainerInterface $container)
/**
* Returns the ReflectionMethod for the given controller string.
- *
- * @return \ReflectionMethod|null
*/
- public function getReflectionMethod($controller)
+ public function getReflectionMethod($controller): ?\ReflectionMethod
{
if (is_string($controller)) {
$controller = $this->getClassAndMethod($controller);
- if (null === $controller) {
- return null;
- }
+ }
+
+ if (null === $controller) {
+ return null;
}
return $this->geReflectionMethodByClassNameAndMethodName(...$controller);
}
- /**
- * @return \ReflectionMethod|null
- */
- public function geReflectionMethodByClassNameAndMethodName(string $class, string $method)
+ public function geReflectionMethodByClassNameAndMethodName(string $class, string $method): ?\ReflectionMethod
{
try {
return new \ReflectionMethod($class, $method);
- } catch (\ReflectionException $e) {
+ } catch (\ReflectionException) {
// In case we can't reflect the controller, we just
// ignore the route
}
@@ -73,14 +64,14 @@ private function getClassAndMethod(string $controller)
return $this->controllers[$controller];
}
- if ($this->controllerNameParser && false === strpos($controller, '::') && 2 === substr_count($controller, ':')) {
+ if ($this->controllerNameParser && !str_contains($controller, '::') && 2 === substr_count($controller, ':')) {
$deprecatedNotation = $controller;
try {
$controller = $this->controllerNameParser->parse($controller);
@trigger_error(sprintf('Referencing controllers with %s is deprecated since Symfony 4.1, use "%s" instead.', $deprecatedNotation, $controller), E_USER_DEPRECATED);
- } catch (\InvalidArgumentException $e) {
+ } catch (\InvalidArgumentException) {
// unable to optimize unknown notation
}
}
@@ -91,7 +82,7 @@ private function getClassAndMethod(string $controller)
// Since symfony 4.1 routes are defined like service_id::method_name
if (Kernel::VERSION_ID >= 40100 && !class_exists($class)) {
if ($this->container->has($class)) {
- $class = get_class($this->container->get($class));
+ $class = $this->container->get($class)::class;
if (class_exists(ClassUtils::class)) {
$class = ClassUtils::getRealClass($class);
}
@@ -108,7 +99,7 @@ private function getClassAndMethod(string $controller)
}
if ($this->container->has($controller)) {
- $class = get_class($this->container->get($controller));
+ $class = $this->container->get($controller)::class;
if (class_exists(ClassUtils::class)) {
$class = ClassUtils::getRealClass($class);
}
@@ -122,7 +113,7 @@ private function getClassAndMethod(string $controller)
if (!isset($class) || !isset($method)) {
$this->controllers[$controller] = null;
- return;
+ return null;
}
return $this->controllers[$controller] = [$class, $method];
diff --git a/Util/SetsContextTrait.php b/Util/SetsContextTrait.php
new file mode 100644
index 000000000..394de3cd4
--- /dev/null
+++ b/Util/SetsContextTrait.php
@@ -0,0 +1,24 @@
+=7.1.3",
+ "php": ">=8.0",
"ext-json": "*",
"doctrine/annotations": "^1.11",
"psr/cache": "^1.0|^2.0|^3.0",
"psr/container": "^1.0|^2.0",
"psr/log": "^1.0|^2.0|^3.0",
- "symfony/config": "^4.4|^5.0",
- "symfony/console": "^4.4|^5.0",
- "symfony/dependency-injection": "^4.4|^5.0",
- "symfony/framework-bundle": "^4.4|^5.0",
- "symfony/http-foundation": "^4.4|^5.0",
- "symfony/http-kernel": "^4.4|^5.0",
- "symfony/options-resolver": "^4.4|^5.0",
- "symfony/property-info": "^4.4|^5.0",
- "symfony/routing": "^4.4|^5.0",
- "zircote/swagger-php": "^3.0",
+ "symfony/config": "^5.3|^6.0",
+ "symfony/console": "^5.3|^6.0",
+ "symfony/dependency-injection": "^5.3|^6.0",
+ "symfony/framework-bundle": "^5.3|^6.0",
+ "symfony/http-foundation": "^5.3|^6.0",
+ "symfony/http-kernel": "^5.3|^6.0",
+ "symfony/options-resolver": "^5.3|^6.0",
+ "symfony/property-info": "^5.3|^6.0",
+ "symfony/routing": "^5.3|^6.0",
+ "zircote/swagger-php": "^3.2|^4.0",
"phpdocumentor/reflection-docblock": "^3.1|^4.4|^5.0"
},
"require-dev": {
- "sensio/framework-extra-bundle": "^4.4|^5.0|^6.0",
- "symfony/asset": "^4.4|^5.0",
- "symfony/dom-crawler": "^4.4|^5.0",
- "symfony/browser-kit": "^4.4|^5.0",
- "symfony/cache": "^4.4|^5.0",
- "symfony/form": "^4.4|^5.0",
- "symfony/phpunit-bridge": "^5.2",
- "symfony/property-access": "^4.4|^5.0",
- "symfony/serializer": "^4.4|^5.0",
- "symfony/stopwatch": "^4.4|^5.0",
- "symfony/templating": "^4.4|^5.0",
- "symfony/twig-bundle": "^4.4|^5.0",
- "symfony/validator": "^4.4|^5.0",
+ "sensio/framework-extra-bundle": "^5.3|^6.0",
+ "symfony/asset": "^5.3|^6.0",
+ "symfony/dom-crawler": "^5.3|^6.0",
+ "symfony/browser-kit": "^5.3|^6.0",
+ "symfony/cache": "^5.3|^6.0",
+ "symfony/form": "^5.3|^6.0",
+ "symfony/phpunit-bridge": "^5.3|^6.0",
+ "symfony/property-access": "^5.3|^6.0",
+ "symfony/serializer": "^5.3|^6.0",
+ "symfony/stopwatch": "^5.3|^6.0",
+ "symfony/templating": "^5.3|^6.0",
+ "symfony/twig-bundle": "^5.3|^6.0",
+ "symfony/validator": "^5.3|^6.0",
"api-platform/core": "^2.4",
- "friendsofsymfony/rest-bundle": "^2.8|^3.0",
+ "friendsofsymfony/rest-bundle": "^2.8|^3.0|dev-3.*",
"willdurand/hateoas-bundle": "^1.0|^2.0",
"jms/serializer-bundle": "^2.3|^3.0",
"jms/serializer": "^1.14|^3.0",