From 0808e8421a7219d618fd9f05d7a342c72b7a6f0e Mon Sep 17 00:00:00 2001 From: Ilyas Salikhov Date: Tue, 2 Jul 2024 16:24:26 +0300 Subject: [PATCH] Add explicit dependencies instead of container --- Command/DumpCommand.php | 50 ++++++------ Command/SwaggerDumpCommand.php | 35 +++----- Controller/ApiDocController.php | 22 ++--- .../AnnotationsProviderCompilerPass.php | 2 +- .../ExtractorHandlerCompilerPass.php | 2 +- Extractor/ApiDocExtractor.php | 80 ++++--------------- Extractor/CachingApiDocExtractor.php | 36 +++------ Resources/config/autowired.yaml | 12 ++- Resources/config/services.xml | 2 +- 9 files changed, 83 insertions(+), 158 deletions(-) diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php index 3a4d684..522d157 100644 --- a/Command/DumpCommand.php +++ b/Command/DumpCommand.php @@ -12,12 +12,15 @@ namespace Nelmio\ApiDocBundle\Command; use Nelmio\ApiDocBundle\Annotation\ApiDoc; +use Nelmio\ApiDocBundle\Extractor\ApiDocExtractor; +use Nelmio\ApiDocBundle\Formatter\HtmlFormatter; +use Nelmio\ApiDocBundle\Formatter\MarkdownFormatter; +use Nelmio\ApiDocBundle\Formatter\SimpleFormatter; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -27,29 +30,28 @@ use Symfony\Contracts\Translation\TranslatorInterface; )] class DumpCommand extends Command { - /** - * @var array - */ - protected $availableFormats = array('markdown', 'json', 'html'); + private const AVAILABLE_FORMATS = ['markdown', 'json', 'html']; /** * @param TranslatorInterface&LocaleAwareInterface $translator */ public function __construct( - private ContainerInterface $container, - private TranslatorInterface $translator, - string $name = null + private readonly SimpleFormatter $simpleFormatter, + private readonly MarkdownFormatter $markdownFormatter, + private readonly HtmlFormatter $htmlFormatter, + private readonly ApiDocExtractor $apiDocExtractor, + private readonly TranslatorInterface $translator ) { - parent::__construct($name); + parent::__construct(); } - protected function configure() + protected function configure(): void { $this ->addOption( 'format', '', InputOption::VALUE_REQUIRED, - 'Output format like: ' . implode(', ', $this->availableFormats), - $this->availableFormats[0] + 'Output format like: ' . implode(', ', self::AVAILABLE_FORMATS), + self::AVAILABLE_FORMATS[0] ) ->addOption('api-version', null, InputOption::VALUE_REQUIRED, 'The API version') ->addOption('locale', null, InputOption::VALUE_REQUIRED, 'Locale for translation') @@ -63,15 +65,12 @@ class DumpCommand extends Command $format = $input->getOption('format'); $view = $input->getOption('view'); - if ($format === 'json') { - $formatter = $this->container->get('nelmio_api_doc.formatter.simple_formatter'); - } else { - if (!in_array($format, $this->availableFormats)) { - throw new \RuntimeException(sprintf('Format "%s" not supported.', $format)); - } - - $formatter = $this->container->get(sprintf('nelmio_api_doc.formatter.%s_formatter', $format)); - } + $formatter = match ($format) { + 'json' => $this->simpleFormatter, + 'markdown' => $this->markdownFormatter, + 'html' => $this->htmlFormatter, + default => throw new \RuntimeException(sprintf('Format "%s" not supported.', $format)), + }; if ($input->hasOption('locale')) { $this->translator->setLocale($input->getOption('locale') ?? ''); @@ -81,19 +80,18 @@ class DumpCommand extends Command $formatter->setVersion($input->getOption('api-version')); } - if ($input->getOption('no-sandbox') && 'html' === $format) { + if ($formatter instanceof HtmlFormatter && $input->getOption('no-sandbox')) { $formatter->setEnableSandbox(false); } - $extractor = $this->container->get('nelmio_api_doc.extractor.api_doc_extractor'); $extractedDoc = $input->hasOption('api-version') ? - $extractor->allForVersion($input->getOption('api-version'), $view) : - $extractor->all($view); + $this->apiDocExtractor->allForVersion($input->getOption('api-version'), $view) : + $this->apiDocExtractor->all($view); $formattedDoc = $formatter->format($extractedDoc); if ('json' === $format) { - $output->writeln(json_encode($formattedDoc)); + $output->writeln(json_encode($formattedDoc, JSON_THROW_ON_ERROR)); } else { $output->writeln($formattedDoc, OutputInterface::OUTPUT_RAW); } diff --git a/Command/SwaggerDumpCommand.php b/Command/SwaggerDumpCommand.php index 4146df0..d266dc5 100644 --- a/Command/SwaggerDumpCommand.php +++ b/Command/SwaggerDumpCommand.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Command; +use Nelmio\ApiDocBundle\Extractor\ApiDocExtractor; use Nelmio\ApiDocBundle\Formatter\SwaggerFormatter; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -18,7 +19,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; @@ -33,24 +33,16 @@ use Symfony\Component\Filesystem\Filesystem; )] class SwaggerDumpCommand extends Command { - /** - * @var Filesystem - */ - protected $filesystem; - - /** - * @var SwaggerFormatter - */ - protected $formatter; + private Filesystem $filesystem; public function __construct( - private ContainerInterface $container, - string $name = null + private readonly ApiDocExtractor $extractor, + private readonly SwaggerFormatter $formatter ) { - parent::__construct($name); + parent::__construct(); } - protected function configure() + protected function configure(): void { $this->filesystem = new Filesystem(); @@ -64,24 +56,21 @@ class SwaggerDumpCommand extends Command protected function execute(InputInterface $input, OutputInterface $output): int { - $extractor = $this->container->get('nelmio_api_doc.extractor.api_doc_extractor'); - $this->formatter = $this->container->get('nelmio_api_doc.formatter.swagger_formatter'); - if ($input->getOption('list-only') && $input->getOption('resource')) { throw new \RuntimeException('Cannot selectively dump a resource with the --list-only flag.'); } - $apiDocs = $extractor->all(); + $apiDocs = $this->extractor->all(); if ($input->getOption('list-only')) { - $data = $this->getResourceList($apiDocs, $output); + $data = $this->getResourceList($apiDocs); $this->dump($data, null, $input, $output); return 0; } - if (false != ($resource = $input->getOption('resource'))) { - $data = $this->getApiDeclaration($apiDocs, $resource, $output); + if (false !== ($resource = $input->getOption('resource'))) { + $data = $this->getApiDeclaration($apiDocs, $resource); if (count($data['apis']) === 0) { throw new \InvalidArgumentException(sprintf('Resource "%s" does not exist.', $resource)); } @@ -116,7 +105,7 @@ class SwaggerDumpCommand extends Command return 0; } - protected function dump(array $data, $resource, InputInterface $input, OutputInterface $output, $treatAsFile = true) + protected function dump(array $data, $resource, InputInterface $input, OutputInterface $output, $treatAsFile = true): void { $destination = $input->getArgument('destination'); @@ -154,7 +143,7 @@ class SwaggerDumpCommand extends Command } - protected function writeToFile($content, $file, OutputInterface $output, $message) + protected function writeToFile($content, $file, OutputInterface $output, $message): void { try { $this->filesystem->dumpFile($file, $content); diff --git a/Controller/ApiDocController.php b/Controller/ApiDocController.php index 4125d52..7d3109f 100644 --- a/Controller/ApiDocController.php +++ b/Controller/ApiDocController.php @@ -11,9 +11,11 @@ namespace Nelmio\ApiDocBundle\Controller; +use Nelmio\ApiDocBundle\Extractor\ApiDocExtractor; +use Nelmio\ApiDocBundle\Formatter\HtmlFormatter; use Nelmio\ApiDocBundle\Formatter\RequestAwareSwaggerFormatter; use Nelmio\ApiDocBundle\Annotation\ApiDoc; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Nelmio\ApiDocBundle\Formatter\SwaggerFormatter; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -22,23 +24,23 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class ApiDocController extends AbstractController { public function __construct( - private ContainerInterface $c + private readonly ApiDocExtractor $extractor, + private readonly HtmlFormatter $htmlFormatter, + private readonly SwaggerFormatter $swaggerFormatter ) { } public function index(Request $request, $view = ApiDoc::DEFAULT_VIEW) { - $extractor = $this->c->get('nelmio_api_doc.extractor.api_doc_extractor'); - $formatter = $this->c->get('nelmio_api_doc.formatter.html_formatter'); $apiVersion = $request->query->get('_version', null); if ($apiVersion) { - $formatter->setVersion($apiVersion); - $extractedDoc = $extractor->allForVersion($apiVersion, $view); + $this->htmlFormatter->setVersion($apiVersion); + $extractedDoc = $this->extractor->allForVersion($apiVersion, $view); } else { - $extractedDoc = $extractor->all($view); + $extractedDoc = $this->extractor->all($view); } - $htmlContent = $formatter->format($extractedDoc); + $htmlContent = $this->htmlFormatter->format($extractedDoc); return new Response($htmlContent, 200, array('Content-Type' => 'text/html')); } @@ -46,8 +48,8 @@ class ApiDocController extends AbstractController public function swagger(Request $request, $resource = null) { - $docs = $this->c->get('nelmio_api_doc.extractor.api_doc_extractor')->all(); - $formatter = new RequestAwareSwaggerFormatter($request, $this->c->get('nelmio_api_doc.formatter.swagger_formatter')); + $docs = $this->extractor->all(); + $formatter = new RequestAwareSwaggerFormatter($request, $this->swaggerFormatter); $spec = $formatter->format($docs, $resource ? '/' . $resource : null); diff --git a/DependencyInjection/AnnotationsProviderCompilerPass.php b/DependencyInjection/AnnotationsProviderCompilerPass.php index c5feb6b..abd2a9c 100644 --- a/DependencyInjection/AnnotationsProviderCompilerPass.php +++ b/DependencyInjection/AnnotationsProviderCompilerPass.php @@ -34,7 +34,7 @@ class AnnotationsProviderCompilerPass implements CompilerPassInterface $container ->getDefinition('nelmio_api_doc.extractor.api_doc_extractor') - ->replaceArgument(5, $annotationsProviders) + ->replaceArgument(4, $annotationsProviders) ; } } diff --git a/DependencyInjection/ExtractorHandlerCompilerPass.php b/DependencyInjection/ExtractorHandlerCompilerPass.php index 50c7143..efba3a4 100644 --- a/DependencyInjection/ExtractorHandlerCompilerPass.php +++ b/DependencyInjection/ExtractorHandlerCompilerPass.php @@ -29,6 +29,6 @@ class ExtractorHandlerCompilerPass implements CompilerPassInterface $container ->getDefinition('nelmio_api_doc.extractor.api_doc_extractor') - ->replaceArgument(4, $handlers); + ->replaceArgument(3, $handlers); } } diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 9d65f74..8574278 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -12,65 +12,36 @@ namespace Nelmio\ApiDocBundle\Extractor; use Doctrine\Common\Annotations\Reader; -use Doctrine\Common\Util\ClassUtils; use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\DataTypes; use Nelmio\ApiDocBundle\Parser\ParserInterface; use Nelmio\ApiDocBundle\Parser\PostParserInterface; use Nelmio\ApiDocBundle\Util\DocCommentExtractor; -use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouterInterface; class ApiDocExtractor { - const ANNOTATION_CLASS = 'Nelmio\\ApiDocBundle\\Annotation\\ApiDoc'; - - /** - * @var ContainerInterface - */ - protected $container; - - /** - * @var RouterInterface - */ - protected $router; - - /** - * @var Reader - */ - protected $reader; - - /** - * @var DocCommentExtractor - */ - private $commentExtractor; + public const ANNOTATION_CLASS = ApiDoc::class; /** * @var ParserInterface[] */ - protected $parsers = array(); + protected array $parsers = []; /** - * @var HandlerInterface[] + * @param HandlerInterface[] $handlers + * @param AnnotationsProviderInterface[] $annotationsProviders + * @param string[] $excludeSections */ - protected $handlers; - - /** - * @var AnnotationsProviderInterface[] - */ - protected $annotationsProviders; - - public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader, DocCommentExtractor $commentExtractor, array $handlers, array $annotationsProviders) - { - $this->container = $container; - $this->router = $router; - $this->reader = $reader; - $this->commentExtractor = $commentExtractor; - $this->handlers = $handlers; - $this->annotationsProviders = $annotationsProviders; + public function __construct( + protected RouterInterface $router, + protected Reader $reader, + protected DocCommentExtractor $commentExtractor, + protected array $handlers, + protected array $annotationsProviders, + protected array $excludeSections + ) { } /** @@ -131,7 +102,6 @@ class ApiDocExtractor { $array = array(); $resources = array(); - $excludeSections = $this->container->getParameter('nelmio_api_doc.exclude_sections'); foreach ($routes as $route) { if (!$route instanceof Route) { @@ -141,7 +111,7 @@ class ApiDocExtractor if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) { $annotation = $this->reader->getMethodAnnotation($method, static::ANNOTATION_CLASS); if ( - $annotation && !in_array($annotation->getSection(), $excludeSections) && + $annotation && !in_array($annotation->getSection(), $this->excludeSections) && (in_array($view, $annotation->getViews()) || (0 === count($annotation->getViews()) && $view === ApiDoc::DEFAULT_VIEW)) ) { if ($annotation->isResource()) { @@ -228,28 +198,6 @@ class ApiDocExtractor if (preg_match('#(.+)::([\w]+)#', $controller, $matches)) { $class = $matches[1]; $method = $matches[2]; - } else { - if (preg_match('#(.+):([\w]+)#', $controller, $matches)) { - $controller = $matches[1]; - $method = $matches[2]; - } - - if ($this->container->has($controller)) { - // BC SF < 3.0 - if (method_exists($this->container, 'enterScope')) { - $this->container->enterScope('request'); - $this->container->set('request', new Request(), 'request'); - } - $class = ClassUtils::getRealClass(get_class($this->container->get($controller))); - // BC SF < 3.0 - if (method_exists($this->container, 'enterScope')) { - $this->container->leaveScope('request'); - } - - if (!isset($method) && method_exists($class, '__invoke')) { - $method = '__invoke'; - } - } } if (isset($class) && isset($method)) { diff --git a/Extractor/CachingApiDocExtractor.php b/Extractor/CachingApiDocExtractor.php index dbb83fa..fc628e6 100644 --- a/Extractor/CachingApiDocExtractor.php +++ b/Extractor/CachingApiDocExtractor.php @@ -14,10 +14,8 @@ namespace Nelmio\ApiDocBundle\Extractor; use Doctrine\Common\Annotations\Reader; use Nelmio\ApiDocBundle\Util\DocCommentExtractor; use Nelmio\ApiDocBundle\Annotation\ApiDoc; -use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\RouterInterface; /** @@ -28,39 +26,23 @@ use Symfony\Component\Routing\RouterInterface; class CachingApiDocExtractor extends ApiDocExtractor { /** - * @var string - */ - private $cacheFile; - - /** - * @var bool - */ - private $debug; - - /** - * @param ContainerInterface $container - * @param RouterInterface $router - * @param Reader $reader - * @param DocCommentExtractor $commentExtractor - * @param array $handlers - * @param array $annotationsProviders - * @param string $cacheFile - * @param bool|false $debug + * @param HandlerInterface[] $handlers + * @param AnnotationsProviderInterface[] $annotationsProviders + * @param string[] $excludeSections + * @param string $cacheFile + * @param bool|false $debug */ public function __construct( - ContainerInterface $container, RouterInterface $router, Reader $reader, DocCommentExtractor $commentExtractor, array $handlers, array $annotationsProviders, - $cacheFile, - $debug = false + array $excludeSections, + private string $cacheFile, + private bool $debug = false ) { - parent::__construct($container, $router, $reader, $commentExtractor, $handlers, $annotationsProviders); - - $this->cacheFile = $cacheFile; - $this->debug = $debug; + parent::__construct($router, $reader, $commentExtractor, $handlers, $annotationsProviders, $excludeSections); } /** diff --git a/Resources/config/autowired.yaml b/Resources/config/autowired.yaml index 6acb989..137ecee 100644 --- a/Resources/config/autowired.yaml +++ b/Resources/config/autowired.yaml @@ -6,12 +6,18 @@ services: Nelmio\ApiDocBundle\Command\DumpCommand: arguments: - $container: '@service_container' + $simpleFormatter: '@nelmio_api_doc.formatter.simple_formatter' + $markdownFormatter: '@nelmio_api_doc.formatter.markdown_formatter' + $htmlFormatter: '@nelmio_api_doc.formatter.html_formatter' + $apiDocExtractor: '@nelmio_api_doc.extractor.api_doc_extractor' Nelmio\ApiDocBundle\Command\SwaggerDumpCommand: arguments: - $container: '@service_container' + $extractor: '@nelmio_api_doc.extractor.api_doc_extractor' + $formatter: '@nelmio_api_doc.formatter.swagger_formatter' Nelmio\ApiDocBundle\Controller\ApiDocController: arguments: - $c: '@service_container' + $extractor: '@nelmio_api_doc.extractor.api_doc_extractor' + $htmlFormatter: '@nelmio_api_doc.formatter.html_formatter' + $swaggerFormatter: '@nelmio_api_doc.formatter.swagger_formatter' diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 2440f5e..3230654 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -27,12 +27,12 @@ - + %nelmio_api_doc.exclude_sections%