bug #34935 [FrameworkBundle][DependencyInjection] Skip removed ids in the lint container command and its associated pass (fancyweb)

This PR was merged into the 4.4 branch.

Discussion
----------

[FrameworkBundle][DependencyInjection] Skip removed ids in the lint container command and its associated pass

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | https://github.com/symfony/symfony/issues/34858
| License       | MIT
| Doc PR        | -

We remove the "removing" passes again and to avoid what https://github.com/symfony/symfony/pull/34502 fixed, we skip validating the "live" container removed ids in the pass (the "live" container is supposed to have the same definitions than the "debug container" one).

Logically, an errored service cannot pass the "live" container compilation without being removed. Consequently, it also skips the errored services that ended up being removed in the "live" container.

Commits
-------

a0f581ba9d [FrameworkBundle][DependencyInjection] Skip removed ids in the lint container command and its associated pass
This commit is contained in:
Nicolas Grekas 2019-12-13 13:19:04 +01:00
commit da7dedaaa0
3 changed files with 60 additions and 10 deletions

View File

@ -14,13 +14,17 @@ namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
use Symfony\Component\HttpKernel\Kernel;
final class ContainerLintCommand extends Command
{
@ -47,13 +51,18 @@ final class ContainerLintCommand extends Command
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$container = $this->getContainerBuilder();
$io = new SymfonyStyle($input, $output);
$errorIo = $io->getErrorStyle();
try {
$container = $this->getContainerBuilder();
} catch (RuntimeException $e) {
$errorIo->error($e->getMessage());
return 2;
}
$container->setParameter('container.build_hash', 'lint_container');
$container->setParameter('container.build_time', time());
$container->setParameter('container.build_id', 'lint_container');
$container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100);
$container->compile();
@ -67,22 +76,44 @@ final class ContainerLintCommand extends Command
}
$kernel = $this->getApplication()->getKernel();
$kernelContainer = $kernel->getContainer();
if (!$kernel->isDebug() || !(new ConfigCache($kernelContainer->getParameter('debug.container.dump'), true))->isFresh()) {
if (!$kernel instanceof Kernel) {
throw new RuntimeException("This command does not support the console application's kernel.");
}
if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) {
$buildContainer = \Closure::bind(function (): ContainerBuilder {
$this->initializeBundles();
return $this->buildContainer();
}, $kernel, \get_class($kernel));
$container = $buildContainer();
$skippedIds = [];
} else {
(new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump'));
if (!$kernelContainer instanceof Container) {
throw new RuntimeException("This command does not support the console application kernel's container.");
}
(new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernelContainer->getParameter('debug.container.dump'));
$refl = new \ReflectionProperty($parameterBag, 'resolved');
$refl->setAccessible(true);
$refl->setValue($parameterBag, true);
$passConfig = $container->getCompilerPassConfig();
$passConfig->setRemovingPasses([]);
$passConfig->setAfterRemovingPasses([]);
$skippedIds = $kernelContainer->getRemovedIds();
}
$container->setParameter('container.build_hash', 'lint_container');
$container->setParameter('container.build_id', 'lint_container');
$container->addCompilerPass(new CheckTypeDeclarationsPass(true, $skippedIds), PassConfig::TYPE_AFTER_REMOVING, -100);
return $this->containerBuilder = $container;
}
}

View File

@ -42,16 +42,19 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
private const SCALAR_TYPES = ['int', 'float', 'bool', 'string'];
private $autoload;
private $skippedIds;
private $expressionLanguage;
/**
* @param bool $autoload Whether services who's class in not loaded should be checked or not.
* Defaults to false to save loading code during compilation.
* @param bool $autoload Whether services who's class in not loaded should be checked or not.
* Defaults to false to save loading code during compilation.
* @param array $skippedIds An array indexed by the service ids to skip
*/
public function __construct(bool $autoload = false)
public function __construct(bool $autoload = false, array $skippedIds = [])
{
$this->autoload = $autoload;
$this->skippedIds = $skippedIds;
}
/**
@ -59,6 +62,10 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
*/
protected function processValue($value, $isRoot = false)
{
if (isset($this->skippedIds[$this->currentId])) {
return $value;
}
if (!$value instanceof Definition || $value->hasErrors()) {
return parent::processValue($value, $isRoot);
}

View File

@ -669,4 +669,16 @@ class CheckTypeDeclarationsPassTest extends TestCase
$this->addToAssertionCount(1);
}
public function testProcessSkipSkippedIds()
{
$container = new ContainerBuilder();
$container
->register('foobar', BarMethodCall::class)
->addMethodCall('setArray', ['a string']);
(new CheckTypeDeclarationsPass(true, ['foobar' => true]))->process($container);
$this->addToAssertionCount(1);
}
}