Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drastically improved debug:autowiring + new autowiring info system #26142

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -15,6 +15,8 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Debug\AutowiringInfoManager;
use Symfony\Component\DependencyInjection\Debug\AutowiringTypeInfo;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this isn't used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are both used - one I think just for phpdoc :). We have a PR bot (fabbot) that validates cs, including unused use statements

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh how come include namespaces for just docblocks? Interesting!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It lets you use the short class names in phpdoc - like @param Foo - editors will use the use statement to know the full class name that you’re referencing :)


/**
* A console command for autowiring information.
Expand All @@ -27,6 +29,15 @@ class DebugAutowiringCommand extends ContainerDebugCommand
{
protected static $defaultName = 'debug:autowiring';

private $autowiringInfoManager;

public function __construct(AutowiringInfoManager $autowiringInfoManager = null)
{
$this->autowiringInfoManager = $autowiringInfoManager;

parent::__construct();
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -78,24 +89,92 @@ protected function execute(InputInterface $input, OutputInterface $output)

asort($serviceIds);

$io->title('Autowirable Services');
$io->text('The following classes & interfaces can be used as type-hints when autowiring:');
if ($search) {
$io->text(sprintf('(only showing classes/interfaces matching <comment>%s</comment>)', $search));
}
$io->newLine();
$tableRows = array();

$keyServices = array();
$otherServices = array();
$hasAlias = array();
foreach ($serviceIds as $serviceId) {
if (null !== $this->autowiringInfoManager && $autowiringInfo = $this->autowiringInfoManager->getInfo($serviceId)) {
$keyServices[] = array(
'info' => $autowiringInfo,
'alias' => $builder->has($serviceId) ? $builder->getAlias($serviceId) : null,
);

continue;
}

if ($builder->hasAlias($serviceId)) {
$tableRows[] = array(sprintf('<fg=cyan>%s</fg=cyan>', $serviceId));
$tableRows[] = array(sprintf(' alias to %s', $builder->getAlias($serviceId)));
$hasAlias[(string) $builder->getAlias($serviceId)] = true;
} else {
$tableRows[$serviceId] = array(sprintf('<fg=cyan>%s</fg=cyan>', $serviceId));
}

$otherServices[$serviceId] = array(
'type' => $serviceId,
'alias' => $builder->hasAlias($serviceId) ? $builder->getAlias($serviceId) : null,
);
}
$otherServices = array_diff_key($otherServices, $hasAlias);

usort($keyServices, function ($a, $b) {
if ($a['info']->getPriority() === $b['info']->getPriority()) {
return 0;
}

return $a['info']->getPriority() > $b['info']->getPriority() ? -1 : 1;
});

$this->printKeyServices($keyServices, $io);

$io->table(array(), array_diff_key($tableRows, $hasAlias));
$this->printOtherServices($otherServices, $io);
}

private function printOtherServices(array $otherServices, SymfonyStyle $io)
{
if (empty($otherServices)) {
return;
}

// not necessary to print if this is the only list
if (null !== $this->autowiringInfoManager) {
$io->title('Other Services');
}

foreach ($otherServices as $serviceData) {
$io->writeln(sprintf('<fg=cyan>%s</fg=cyan>', $serviceData['type']));
if ($alias = $serviceData['alias']) {
$io->writeln(sprintf(' alias to %s', $alias));
}
}
}

private function printKeyServices(array $keyServices, SymfonyStyle $io)
{
if (empty($keyServices)) {
return;
}

$io->title('Key Services');
Copy link
Contributor

@ro0NL ro0NL Feb 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd say Main Services / Common Services / General Services or so =/

foreach ($keyServices as $serviceData) {
/** @var AutowiringTypeInfo $info */
$info = $serviceData['info'];

$nameLine = sprintf('<comment>%s</comment>', $info->getName());
if ($info->getDescription()) {
$nameLine .= sprintf(' (%s)', $info->getDescription());
}
$io->writeln($nameLine);

$io->writeln(sprintf(' Type: <fg=cyan>%s</fg=cyan>', $info->getType()));

if ($serviceData['alias']) {
$io->writeln(sprintf(' Alias to the %s service', $serviceData['alias']));
}

$io->writeln('');
}
}
}
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* Looks for & processes debug.autowiring_info_provider tags.
*
* @author Ryan Weaver <ryan@knpuniversity.com>
*/
class AutowireDebugInfoPass implements CompilerPassInterface
{
const AUTOWIRING_INFO_PROVIDER_TAG = 'debug.autowiring_info_provider';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public const But needed? This is the first tag const right...


public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('debug.autowiring_info_manager')) {
return;
}

$definition = $container->getDefinition('debug.autowiring_info_manager');

$references = array();
foreach ($container->findTaggedServiceIds(self::AUTOWIRING_INFO_PROVIDER_TAG, true) as $serviceId => $attributes) {
$references[] = new Reference($serviceId);
}

$definition->replaceArgument(0, $references);
}
}
@@ -0,0 +1,105 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;

use Doctrine\Common\Annotations\Reader;
use Psr\Cache\CacheItemPoolInterface;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Asset\Packages;
use Symfony\Component\DependencyInjection\Debug\AutowiringInfoProviderInterface;
use Symfony\Component\DependencyInjection\Debug\AutowiringTypeInfo;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Workflow\Registry;

/**
* @author Ryan Weaver <ryan@knpuniversity.com>
*/
final class FrameworkAutowiringInfoProvider implements AutowiringInfoProviderInterface
{
public function getTypeInfos(): array
{
return array(
AutowiringTypeInfo::create(CacheItemPoolInterface::class, 'Cache', 10)
->setDescription('general-purpose service for caching things'),

AutowiringTypeInfo::create(CacheInterface::class, 'Simple Cache', 10)
->setDescription('simpler cache, but less features'),

AutowiringTypeInfo::create(RouterInterface::class, 'Router', 10)
->setDescription('used to generate URLs'),

AutowiringTypeInfo::create(EventDispatcherInterface::class, 'Event Dispatcher')
->setDescription('used to dispatch custom events'),

AutowiringTypeInfo::create(Reader::class, 'Annotation Reader', -10),

AutowiringTypeInfo::create(ParameterBagInterface::class, 'Parameter Bag')
->setDescription('access service parameters'),

AutowiringTypeInfo::create(Filesystem::class, 'Filesystem')
->setDescription('helper for filesystem actions'),

AutowiringTypeInfo::create(RequestStack::class, 'Request Stack')
->setDescription('access the Request object'),

AutowiringTypeInfo::create(SessionInterface::class, 'Session'),

AutowiringTypeInfo::create(FlashBagInterface::class, 'Flash Bag')
->setDescription('use to set temporary success/failure messages'),

AutowiringTypeInfo::create(KernelInterface::class, 'Kernel'),

AutowiringTypeInfo::create(Stopwatch::class, 'Stopwatch')
->setDescription('use to add custom timings to profiler'),

AutowiringTypeInfo::create(Packages::class, 'Asset Packages')
->setDescription('use to generate URLs to assets'),

AutowiringTypeInfo::create(FormFactoryInterface::class, 'Form Factory')
->setDescription('use to create form objects'),

AutowiringTypeInfo::create(ValidatorInterface::class, 'Validator')
->setDescription('use to validate data against some constraints'),

AutowiringTypeInfo::create(TranslatorInterface::class, 'Translator'),

AutowiringTypeInfo::create(PropertyAccessorInterface::class, 'Property Accessor')
->setDescription('use to read dynamic keys from some data'),

AutowiringTypeInfo::create(CsrfTokenManagerInterface::class, 'CSRF Token Manager')
->setDescription('generate and check CSRF tokens'),

AutowiringTypeInfo::create(SerializerInterface::class, 'Serializer')
->setDescription('use to serialize data to JSON, XML, etc'),

AutowiringTypeInfo::create(Registry::class, 'Workflow')
->setDescription('use to fetch workflows'),

AutowiringTypeInfo::create(Registry::class, 'Workflow')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicated :)

->setDescription('use to fetch workflows'),
);
}
}
Expand Up @@ -16,6 +16,7 @@
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AutowireDebugInfoPass;
use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader;
use Symfony\Bundle\FullStack;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
Expand All @@ -33,6 +34,7 @@
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Debug\AutowiringInfoProviderInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -330,6 +332,8 @@ public function load(array $configs, ContainerBuilder $container)
->addTag('validator.constraint_validator');
$container->registerForAutoconfiguration(ObjectInitializerInterface::class)
->addTag('validator.initializer');
$container->registerForAutoconfiguration(AutowiringInfoProviderInterface::class)
->addTag(AutowireDebugInfoPass::AUTOWIRING_INFO_PROVIDER_TAG);

if (!$container->getParameter('kernel.debug')) {
// remove tagged iterator argument for resource checkers
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -13,6 +13,7 @@

use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddDebugLogProcessorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AutowireDebugInfoPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheCollectorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolClearerPass;
Expand Down Expand Up @@ -120,6 +121,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_BEFORE_REMOVING, -255);
$container->addCompilerPass(new CacheCollectorPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new AutowireDebugInfoPass());
}
}

Expand Down
Expand Up @@ -56,6 +56,7 @@
</service>

<service id="console.command.debug_autowiring" class="Symfony\Bundle\FrameworkBundle\Command\DebugAutowiringCommand">
<argument type="service" id="debug.autowiring_info_manager" on-invalid="ignore" />
<tag name="console.command" command="debug:autowiring" />
</service>

Expand Down
8 changes: 8 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
Expand Up @@ -23,5 +23,13 @@
<argument type="service" id="debug.argument_resolver.inner" />
<argument type="service" id="debug.stopwatch" />
</service>

<service id="debug.autowiring_info_manager" class="Symfony\Component\DependencyInjection\Debug\AutowiringInfoManager">
<argument /> <!-- argument info providers -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type="tagged"?

</service>

<service id="debug.autowiring.framework_autowiring_info_provider" class="Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkAutowiringInfoProvider">
<tag name="debug.autowiring_info_provider" />
</service>
</services>
</container>
Expand Up @@ -31,6 +31,7 @@ public function testBasicFunctionality()

$this->assertContains('Symfony\Component\HttpKernel\HttpKernelInterface', $tester->getDisplay());
$this->assertContains('alias to http_kernel', $tester->getDisplay());
$this->assertContains('Simple Cache', $tester->getDisplay(), 'Key services are displayed');
}

public function testSearchArgument()
Expand Down
@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\SecurityBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\Debug\AutowiringInfoProviderInterface;
use Symfony\Component\DependencyInjection\Debug\AutowiringTypeInfo;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;

/**
* @author Ryan Weaver <ryan@knpuniversity.com>
*/
final class SecurityAutowiringInfoProvider implements AutowiringInfoProviderInterface
{
public function getTypeInfos(): array
{
return array(
AutowiringTypeInfo::create(GuardAuthenticatorHandler::class, 'Guard Auth Handler')
->setDescription('use to manually authenticate with Guard'),

AutowiringTypeInfo::create(Security::class, 'Security')
->setDescription('use to check access & get the current User'),

AutowiringTypeInfo::create(UserPasswordEncoderInterface::class, 'Password Encoder')
->setDescription('use to encode passwords & check them'),
);
}
}
Expand Up @@ -19,5 +19,9 @@
</service>

<service id="security.firewall" alias="debug.security.firewall" />

<service id="debug.autowiring.security_autowiring_info_provider" class="Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityAutowiringInfoProvider">
<tag name="debug.autowiring_info_provider" />
</service>
</services>
</container>