Add explicit dependencies instead of container

This commit is contained in:
Ilyas Salikhov 2024-07-02 16:24:26 +03:00
parent ed2e185fe2
commit 0808e8421a
9 changed files with 83 additions and 158 deletions

View File

@ -12,12 +12,15 @@
namespace Nelmio\ApiDocBundle\Command; namespace Nelmio\ApiDocBundle\Command;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; 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\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
@ -27,29 +30,28 @@ use Symfony\Contracts\Translation\TranslatorInterface;
)] )]
class DumpCommand extends Command class DumpCommand extends Command
{ {
/** private const AVAILABLE_FORMATS = ['markdown', 'json', 'html'];
* @var array
*/
protected $availableFormats = array('markdown', 'json', 'html');
/** /**
* @param TranslatorInterface&LocaleAwareInterface $translator * @param TranslatorInterface&LocaleAwareInterface $translator
*/ */
public function __construct( public function __construct(
private ContainerInterface $container, private readonly SimpleFormatter $simpleFormatter,
private TranslatorInterface $translator, private readonly MarkdownFormatter $markdownFormatter,
string $name = null 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 $this
->addOption( ->addOption(
'format', '', InputOption::VALUE_REQUIRED, 'format', '', InputOption::VALUE_REQUIRED,
'Output format like: ' . implode(', ', $this->availableFormats), 'Output format like: ' . implode(', ', self::AVAILABLE_FORMATS),
$this->availableFormats[0] self::AVAILABLE_FORMATS[0]
) )
->addOption('api-version', null, InputOption::VALUE_REQUIRED, 'The API version') ->addOption('api-version', null, InputOption::VALUE_REQUIRED, 'The API version')
->addOption('locale', null, InputOption::VALUE_REQUIRED, 'Locale for translation') ->addOption('locale', null, InputOption::VALUE_REQUIRED, 'Locale for translation')
@ -63,15 +65,12 @@ class DumpCommand extends Command
$format = $input->getOption('format'); $format = $input->getOption('format');
$view = $input->getOption('view'); $view = $input->getOption('view');
if ($format === 'json') { $formatter = match ($format) {
$formatter = $this->container->get('nelmio_api_doc.formatter.simple_formatter'); 'json' => $this->simpleFormatter,
} else { 'markdown' => $this->markdownFormatter,
if (!in_array($format, $this->availableFormats)) { 'html' => $this->htmlFormatter,
throw new \RuntimeException(sprintf('Format "%s" not supported.', $format)); default => throw new \RuntimeException(sprintf('Format "%s" not supported.', $format)),
} };
$formatter = $this->container->get(sprintf('nelmio_api_doc.formatter.%s_formatter', $format));
}
if ($input->hasOption('locale')) { if ($input->hasOption('locale')) {
$this->translator->setLocale($input->getOption('locale') ?? ''); $this->translator->setLocale($input->getOption('locale') ?? '');
@ -81,19 +80,18 @@ class DumpCommand extends Command
$formatter->setVersion($input->getOption('api-version')); $formatter->setVersion($input->getOption('api-version'));
} }
if ($input->getOption('no-sandbox') && 'html' === $format) { if ($formatter instanceof HtmlFormatter && $input->getOption('no-sandbox')) {
$formatter->setEnableSandbox(false); $formatter->setEnableSandbox(false);
} }
$extractor = $this->container->get('nelmio_api_doc.extractor.api_doc_extractor');
$extractedDoc = $input->hasOption('api-version') ? $extractedDoc = $input->hasOption('api-version') ?
$extractor->allForVersion($input->getOption('api-version'), $view) : $this->apiDocExtractor->allForVersion($input->getOption('api-version'), $view) :
$extractor->all($view); $this->apiDocExtractor->all($view);
$formattedDoc = $formatter->format($extractedDoc); $formattedDoc = $formatter->format($extractedDoc);
if ('json' === $format) { if ('json' === $format) {
$output->writeln(json_encode($formattedDoc)); $output->writeln(json_encode($formattedDoc, JSON_THROW_ON_ERROR));
} else { } else {
$output->writeln($formattedDoc, OutputInterface::OUTPUT_RAW); $output->writeln($formattedDoc, OutputInterface::OUTPUT_RAW);
} }

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Command; namespace Nelmio\ApiDocBundle\Command;
use Nelmio\ApiDocBundle\Extractor\ApiDocExtractor;
use Nelmio\ApiDocBundle\Formatter\SwaggerFormatter; use Nelmio\ApiDocBundle\Formatter\SwaggerFormatter;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; 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\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
@ -33,24 +33,16 @@ use Symfony\Component\Filesystem\Filesystem;
)] )]
class SwaggerDumpCommand extends Command class SwaggerDumpCommand extends Command
{ {
/** private Filesystem $filesystem;
* @var Filesystem
*/
protected $filesystem;
/**
* @var SwaggerFormatter
*/
protected $formatter;
public function __construct( public function __construct(
private ContainerInterface $container, private readonly ApiDocExtractor $extractor,
string $name = null private readonly SwaggerFormatter $formatter
) { ) {
parent::__construct($name); parent::__construct();
} }
protected function configure() protected function configure(): void
{ {
$this->filesystem = new Filesystem(); $this->filesystem = new Filesystem();
@ -64,24 +56,21 @@ class SwaggerDumpCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int 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')) { if ($input->getOption('list-only') && $input->getOption('resource')) {
throw new \RuntimeException('Cannot selectively dump a resource with the --list-only flag.'); 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')) { if ($input->getOption('list-only')) {
$data = $this->getResourceList($apiDocs, $output); $data = $this->getResourceList($apiDocs);
$this->dump($data, null, $input, $output); $this->dump($data, null, $input, $output);
return 0; return 0;
} }
if (false != ($resource = $input->getOption('resource'))) { if (false !== ($resource = $input->getOption('resource'))) {
$data = $this->getApiDeclaration($apiDocs, $resource, $output); $data = $this->getApiDeclaration($apiDocs, $resource);
if (count($data['apis']) === 0) { if (count($data['apis']) === 0) {
throw new \InvalidArgumentException(sprintf('Resource "%s" does not exist.', $resource)); throw new \InvalidArgumentException(sprintf('Resource "%s" does not exist.', $resource));
} }
@ -116,7 +105,7 @@ class SwaggerDumpCommand extends Command
return 0; 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'); $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 { try {
$this->filesystem->dumpFile($file, $content); $this->filesystem->dumpFile($file, $content);

View File

@ -11,9 +11,11 @@
namespace Nelmio\ApiDocBundle\Controller; namespace Nelmio\ApiDocBundle\Controller;
use Nelmio\ApiDocBundle\Extractor\ApiDocExtractor;
use Nelmio\ApiDocBundle\Formatter\HtmlFormatter;
use Nelmio\ApiDocBundle\Formatter\RequestAwareSwaggerFormatter; use Nelmio\ApiDocBundle\Formatter\RequestAwareSwaggerFormatter;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\DependencyInjection\ContainerInterface; use Nelmio\ApiDocBundle\Formatter\SwaggerFormatter;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -22,23 +24,23 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class ApiDocController extends AbstractController class ApiDocController extends AbstractController
{ {
public function __construct( 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) 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); $apiVersion = $request->query->get('_version', null);
if ($apiVersion) { if ($apiVersion) {
$formatter->setVersion($apiVersion); $this->htmlFormatter->setVersion($apiVersion);
$extractedDoc = $extractor->allForVersion($apiVersion, $view); $extractedDoc = $this->extractor->allForVersion($apiVersion, $view);
} else { } 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')); 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) public function swagger(Request $request, $resource = null)
{ {
$docs = $this->c->get('nelmio_api_doc.extractor.api_doc_extractor')->all(); $docs = $this->extractor->all();
$formatter = new RequestAwareSwaggerFormatter($request, $this->c->get('nelmio_api_doc.formatter.swagger_formatter')); $formatter = new RequestAwareSwaggerFormatter($request, $this->swaggerFormatter);
$spec = $formatter->format($docs, $resource ? '/' . $resource : null); $spec = $formatter->format($docs, $resource ? '/' . $resource : null);

View File

@ -34,7 +34,7 @@ class AnnotationsProviderCompilerPass implements CompilerPassInterface
$container $container
->getDefinition('nelmio_api_doc.extractor.api_doc_extractor') ->getDefinition('nelmio_api_doc.extractor.api_doc_extractor')
->replaceArgument(5, $annotationsProviders) ->replaceArgument(4, $annotationsProviders)
; ;
} }
} }

View File

@ -29,6 +29,6 @@ class ExtractorHandlerCompilerPass implements CompilerPassInterface
$container $container
->getDefinition('nelmio_api_doc.extractor.api_doc_extractor') ->getDefinition('nelmio_api_doc.extractor.api_doc_extractor')
->replaceArgument(4, $handlers); ->replaceArgument(3, $handlers);
} }
} }

View File

@ -12,65 +12,36 @@
namespace Nelmio\ApiDocBundle\Extractor; namespace Nelmio\ApiDocBundle\Extractor;
use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Annotations\Reader;
use Doctrine\Common\Util\ClassUtils;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Nelmio\ApiDocBundle\DataTypes; use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Parser\ParserInterface; use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Nelmio\ApiDocBundle\Parser\PostParserInterface; use Nelmio\ApiDocBundle\Parser\PostParserInterface;
use Nelmio\ApiDocBundle\Util\DocCommentExtractor; 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\Route;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
class ApiDocExtractor class ApiDocExtractor
{ {
const ANNOTATION_CLASS = 'Nelmio\\ApiDocBundle\\Annotation\\ApiDoc'; public const ANNOTATION_CLASS = ApiDoc::class;
/**
* @var ContainerInterface
*/
protected $container;
/**
* @var RouterInterface
*/
protected $router;
/**
* @var Reader
*/
protected $reader;
/**
* @var DocCommentExtractor
*/
private $commentExtractor;
/** /**
* @var ParserInterface[] * @var ParserInterface[]
*/ */
protected $parsers = array(); protected array $parsers = [];
/** /**
* @var HandlerInterface[] * @param HandlerInterface[] $handlers
* @param AnnotationsProviderInterface[] $annotationsProviders
* @param string[] $excludeSections
*/ */
protected $handlers; public function __construct(
protected RouterInterface $router,
/** protected Reader $reader,
* @var AnnotationsProviderInterface[] protected DocCommentExtractor $commentExtractor,
*/ protected array $handlers,
protected $annotationsProviders; protected array $annotationsProviders,
protected array $excludeSections
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;
} }
/** /**
@ -131,7 +102,6 @@ class ApiDocExtractor
{ {
$array = array(); $array = array();
$resources = array(); $resources = array();
$excludeSections = $this->container->getParameter('nelmio_api_doc.exclude_sections');
foreach ($routes as $route) { foreach ($routes as $route) {
if (!$route instanceof Route) { if (!$route instanceof Route) {
@ -141,7 +111,7 @@ class ApiDocExtractor
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) { if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
$annotation = $this->reader->getMethodAnnotation($method, static::ANNOTATION_CLASS); $annotation = $this->reader->getMethodAnnotation($method, static::ANNOTATION_CLASS);
if ( 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)) (in_array($view, $annotation->getViews()) || (0 === count($annotation->getViews()) && $view === ApiDoc::DEFAULT_VIEW))
) { ) {
if ($annotation->isResource()) { if ($annotation->isResource()) {
@ -228,28 +198,6 @@ class ApiDocExtractor
if (preg_match('#(.+)::([\w]+)#', $controller, $matches)) { if (preg_match('#(.+)::([\w]+)#', $controller, $matches)) {
$class = $matches[1]; $class = $matches[1];
$method = $matches[2]; $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)) { if (isset($class) && isset($method)) {

View File

@ -14,10 +14,8 @@ namespace Nelmio\ApiDocBundle\Extractor;
use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiDocBundle\Util\DocCommentExtractor; use Nelmio\ApiDocBundle\Util\DocCommentExtractor;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
/** /**
@ -28,39 +26,23 @@ use Symfony\Component\Routing\RouterInterface;
class CachingApiDocExtractor extends ApiDocExtractor class CachingApiDocExtractor extends ApiDocExtractor
{ {
/** /**
* @var string * @param HandlerInterface[] $handlers
*/ * @param AnnotationsProviderInterface[] $annotationsProviders
private $cacheFile; * @param string[] $excludeSections
* @param string $cacheFile
/** * @param bool|false $debug
* @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
*/ */
public function __construct( public function __construct(
ContainerInterface $container,
RouterInterface $router, RouterInterface $router,
Reader $reader, Reader $reader,
DocCommentExtractor $commentExtractor, DocCommentExtractor $commentExtractor,
array $handlers, array $handlers,
array $annotationsProviders, array $annotationsProviders,
$cacheFile, array $excludeSections,
$debug = false private string $cacheFile,
private bool $debug = false
) { ) {
parent::__construct($container, $router, $reader, $commentExtractor, $handlers, $annotationsProviders); parent::__construct($router, $reader, $commentExtractor, $handlers, $annotationsProviders, $excludeSections);
$this->cacheFile = $cacheFile;
$this->debug = $debug;
} }
/** /**

View File

@ -6,12 +6,18 @@ services:
Nelmio\ApiDocBundle\Command\DumpCommand: Nelmio\ApiDocBundle\Command\DumpCommand:
arguments: 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: Nelmio\ApiDocBundle\Command\SwaggerDumpCommand:
arguments: arguments:
$container: '@service_container' $extractor: '@nelmio_api_doc.extractor.api_doc_extractor'
$formatter: '@nelmio_api_doc.formatter.swagger_formatter'
Nelmio\ApiDocBundle\Controller\ApiDocController: Nelmio\ApiDocBundle\Controller\ApiDocController:
arguments: 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'

View File

@ -27,12 +27,12 @@
</service> </service>
<service id="nelmio_api_doc.extractor.api_doc_extractor" class="%nelmio_api_doc.extractor.api_doc_extractor.class%" public="true"> <service id="nelmio_api_doc.extractor.api_doc_extractor" class="%nelmio_api_doc.extractor.api_doc_extractor.class%" public="true">
<argument type="service" id="service_container" />
<argument type="service" id="router" /> <argument type="service" id="router" />
<argument type="service" id="annotation_reader" /> <argument type="service" id="annotation_reader" />
<argument type="service" id="nelmio_api_doc.doc_comment_extractor" /> <argument type="service" id="nelmio_api_doc.doc_comment_extractor" />
<argument type="collection" /> <argument type="collection" />
<argument type="collection" /> <argument type="collection" />
<argument>%nelmio_api_doc.exclude_sections%</argument>
</service> </service>
<service id="nelmio_api_doc.form.extension.description_form_type_extension" class="%nelmio_api_doc.form.extension.description_form_type_extension.class%"> <service id="nelmio_api_doc.form.extension.description_form_type_extension" class="%nelmio_api_doc.form.extension.description_form_type_extension.class%">