vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.php line 39

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bundle\FrameworkBundle\Command;
  11. use Symfony\Bundle\FrameworkBundle\Console\Descriptor\Descriptor;
  12. use Symfony\Component\Console\Attribute\AsCommand;
  13. use Symfony\Component\Console\Completion\CompletionInput;
  14. use Symfony\Component\Console\Completion\CompletionSuggestions;
  15. use Symfony\Component\Console\Formatter\OutputFormatterStyle;
  16. use Symfony\Component\Console\Input\InputArgument;
  17. use Symfony\Component\Console\Input\InputInterface;
  18. use Symfony\Component\Console\Input\InputOption;
  19. use Symfony\Component\Console\Output\OutputInterface;
  20. use Symfony\Component\Console\Style\SymfonyStyle;
  21. use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
  22. /**
  23.  * A console command for autowiring information.
  24.  *
  25.  * @author Ryan Weaver <ryan@knpuniversity.com>
  26.  *
  27.  * @internal
  28.  */
  29. #[AsCommand(name'debug:autowiring'description'List classes/interfaces you can use for autowiring')]
  30. class DebugAutowiringCommand extends ContainerDebugCommand
  31. {
  32.     private bool $supportsHref;
  33.     private ?FileLinkFormatter $fileLinkFormatter;
  34.     public function __construct(string $name nullFileLinkFormatter $fileLinkFormatter null)
  35.     {
  36.         $this->supportsHref method_exists(OutputFormatterStyle::class, 'setHref');
  37.         $this->fileLinkFormatter $fileLinkFormatter;
  38.         parent::__construct($name);
  39.     }
  40.     /**
  41.      * {@inheritdoc}
  42.      */
  43.     protected function configure()
  44.     {
  45.         $this
  46.             ->setDefinition([
  47.                 new InputArgument('search'InputArgument::OPTIONAL'A search filter'),
  48.                 new InputOption('all'nullInputOption::VALUE_NONE'Show also services that are not aliased'),
  49.             ])
  50.             ->setHelp(<<<'EOF'
  51. The <info>%command.name%</info> command displays the classes and interfaces that
  52. you can use as type-hints for autowiring:
  53.   <info>php %command.full_name%</info>
  54. You can also pass a search term to filter the list:
  55.   <info>php %command.full_name% log</info>
  56. EOF
  57.             )
  58.         ;
  59.     }
  60.     /**
  61.      * {@inheritdoc}
  62.      */
  63.     protected function execute(InputInterface $inputOutputInterface $output): int
  64.     {
  65.         $io = new SymfonyStyle($input$output);
  66.         $errorIo $io->getErrorStyle();
  67.         $builder $this->getContainerBuilder($this->getApplication()->getKernel());
  68.         $serviceIds $builder->getServiceIds();
  69.         $serviceIds array_filter($serviceIds$this->filterToServiceTypes(...));
  70.         if ($search $input->getArgument('search')) {
  71.             $searchNormalized preg_replace('/[^a-zA-Z0-9\x7f-\xff $]++/'''$search);
  72.             $serviceIds array_filter($serviceIds, function ($serviceId) use ($searchNormalized) {
  73.                 return false !== stripos(str_replace('\\'''$serviceId), $searchNormalized) && !str_starts_with($serviceId'.');
  74.             });
  75.             if (empty($serviceIds)) {
  76.                 $errorIo->error(sprintf('No autowirable classes or interfaces found matching "%s"'$search));
  77.                 return 1;
  78.             }
  79.         }
  80.         uasort($serviceIds'strnatcmp');
  81.         $io->title('Autowirable Types');
  82.         $io->text('The following classes & interfaces can be used as type-hints when autowiring:');
  83.         if ($search) {
  84.             $io->text(sprintf('(only showing classes/interfaces matching <comment>%s</comment>)'$search));
  85.         }
  86.         $hasAlias = [];
  87.         $all $input->getOption('all');
  88.         $previousId '-';
  89.         $serviceIdsNb 0;
  90.         foreach ($serviceIds as $serviceId) {
  91.             $text = [];
  92.             $resolvedServiceId $serviceId;
  93.             if (!str_starts_with($serviceId$previousId)) {
  94.                 $text[] = '';
  95.                 if ('' !== $description Descriptor::getClassDescription($serviceId$resolvedServiceId)) {
  96.                     if (isset($hasAlias[$serviceId])) {
  97.                         continue;
  98.                     }
  99.                     $text[] = $description;
  100.                 }
  101.                 $previousId $serviceId.' $';
  102.             }
  103.             $serviceLine sprintf('<fg=yellow>%s</>'$serviceId);
  104.             if ($this->supportsHref && '' !== $fileLink $this->getFileLink($serviceId)) {
  105.                 $serviceLine sprintf('<fg=yellow;href=%s>%s</>'$fileLink$serviceId);
  106.             }
  107.             if ($builder->hasAlias($serviceId)) {
  108.                 $hasAlias[$serviceId] = true;
  109.                 $serviceAlias $builder->getAlias($serviceId);
  110.                 $serviceLine .= ' <fg=cyan>('.$serviceAlias.')</>';
  111.                 if ($serviceAlias->isDeprecated()) {
  112.                     $serviceLine .= ' - <fg=magenta>deprecated</>';
  113.                 }
  114.             } elseif (!$all) {
  115.                 ++$serviceIdsNb;
  116.                 continue;
  117.             }
  118.             $text[] = $serviceLine;
  119.             $io->text($text);
  120.         }
  121.         $io->newLine();
  122.         if ($serviceIdsNb) {
  123.             $io->text(sprintf('%s more concrete service%s would be displayed when adding the "--all" option.'$serviceIdsNb$serviceIdsNb 's' ''));
  124.         }
  125.         if ($all) {
  126.             $io->text('Pro-tip: use interfaces in your type-hints instead of classes to benefit from the dependency inversion principle.');
  127.         }
  128.         $io->newLine();
  129.         return 0;
  130.     }
  131.     private function getFileLink(string $class): string
  132.     {
  133.         if (null === $this->fileLinkFormatter
  134.             || (null === $r $this->getContainerBuilder($this->getApplication()->getKernel())->getReflectionClass($classfalse))) {
  135.             return '';
  136.         }
  137.         return (string) $this->fileLinkFormatter->format($r->getFileName(), $r->getStartLine());
  138.     }
  139.     public function complete(CompletionInput $inputCompletionSuggestions $suggestions): void
  140.     {
  141.         if ($input->mustSuggestArgumentValuesFor('search')) {
  142.             $builder $this->getContainerBuilder($this->getApplication()->getKernel());
  143.             $suggestions->suggestValues(array_filter($builder->getServiceIds(), $this->filterToServiceTypes(...)));
  144.         }
  145.     }
  146. }