diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 79086dbbb8..5a6e3cd425 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG * The `TemplateController` now accepts context argument * Deprecated *not* setting the "framework.router.utf8" configuration option as it will default to `true` in Symfony 6.0 * Added tag `routing.expression_language_function` to define functions available in route conditions + * Added `debug:container --deprecations` command to see compile-time deprecations. 5.0.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 50a8b821f4..43945d3555 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -62,12 +62,17 @@ class ContainerDebugCommand extends Command new InputOption('env-vars', null, InputOption::VALUE_NONE, 'Displays environment variables used in the container'), new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), + new InputOption('deprecations', null, InputOption::VALUE_NONE, 'To output the deprecations generated when compiling and warming the cache'), ]) ->setDescription('Displays current services for an application') ->setHelp(<<<'EOF' The %command.name% command displays all configured public services: php %command.full_name% + +To see deprecations generated during container compilation and cache warmup, use the --deprecations flag: + + php %command.full_name% --deprecations To get specific information about a service, specify its name: @@ -149,6 +154,8 @@ EOF } elseif ($name = $input->getArgument('name')) { $name = $this->findProperServiceName($input, $errorIo, $object, $name, $input->getOption('show-hidden')); $options = ['id' => $name]; + } elseif ($input->getOption('deprecations')) { + $options = ['deprecations' => true]; } else { $options = []; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index fb89768bf5..a1794c350c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -64,6 +64,9 @@ abstract class Descriptor implements DescriptorInterface case $object instanceof ContainerBuilder && isset($options['parameter']): $this->describeContainerParameter($object->resolveEnvPlaceholders($object->getParameter($options['parameter'])), $options); break; + case $object instanceof ContainerBuilder && isset($options['deprecations']): + $this->describeContainerDeprecations($object, $options); + break; case $object instanceof ContainerBuilder: $this->describeContainerServices($object, $options); break; @@ -120,6 +123,8 @@ abstract class Descriptor implements DescriptorInterface */ abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = []); + abstract protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []); + abstract protected function describeContainerDefinition(Definition $definition, array $options = []); abstract protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index b9e5bad0f5..84697e7c91 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -154,6 +154,11 @@ class JsonDescriptor extends Descriptor throw new LogicException('Using the JSON format to debug environment variables is not supported.'); } + protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + { + throw new LogicException('Using the JSON format to print the deprecations is not supported.'); + } + private function writeData(array $data, array $options) { $flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index a04402796b..33bf650e2b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -104,6 +104,11 @@ class MarkdownDescriptor extends Descriptor } } + protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + { + throw new LogicException('Using the Markdown format to print the deprecations is not supported.'); + } + protected function describeContainerServices(ContainerBuilder $builder, array $options = []) { $showHidden = isset($options['show_hidden']) && $options['show_hidden']; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 45c995a467..ddcaaa0336 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -353,6 +353,32 @@ class TextDescriptor extends Descriptor $options['output']->table($tableHeaders, $tableRows); } + protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + { + $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.cache_dir'), $builder->getParameter('kernel.container_class')); + if (!file_exists($containerDeprecationFilePath)) { + $options['output']->warning('The deprecation file does not exist, please try warming the cache first.'); + + return; + } + + $logs = unserialize(file_get_contents($containerDeprecationFilePath)); + if (0 === \count($logs)) { + $options['output']->success('There are no deprecations in the logs!'); + + return; + } + + $formattedLogs = []; + $remainingCount = 0; + foreach ($logs as $log) { + $formattedLogs[] = sprintf("%sx: %s \n in %s:%s", $log['count'], $log['message'], $log['file'], $log['line']); + $remainingCount += $log['count']; + } + $options['output']->title(sprintf('Remaining deprecations (%s)', $remainingCount)); + $options['output']->listing($formattedLogs); + } + protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null) { if ($alias->isPublic()) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index a7f348e76d..3e6708ce86 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -106,6 +106,11 @@ class XmlDescriptor extends Descriptor throw new LogicException('Using the XML format to debug environment variables is not supported.'); } + protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + { + throw new LogicException('Using the XML format to print the deprecations is not supported.'); + } + private function writeDocument(\DOMDocument $dom) { $dom->formatOutput = true; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php index 537ee77174..f4e199da89 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php @@ -136,6 +136,88 @@ TXT $this->assertStringContainsString(file_get_contents(__DIR__.'/Fixtures/describe_env_vars.txt'), $tester->getDisplay(true)); } + public function testGetDeprecation() + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); + $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.cache_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + touch($path); + file_put_contents($path, serialize([[ + 'type' => 16384, + 'message' => 'The "Symfony\Bundle\FrameworkBundle\Controller\Controller" class is deprecated since Symfony 4.2, use Symfony\Bundle\FrameworkBundle\Controller\AbstractController instead.', + 'file' => '/home/hamza/projet/contrib/sf/vendor/symfony/framework-bundle/Controller/Controller.php', + 'line' => 17, + 'trace' => [[ + 'file' => '/home/hamza/projet/contrib/sf/src/Controller/DefaultController.php', + 'line' => 9, + 'function' => 'spl_autoload_call', + ]], + 'count' => 1, + ]])); + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + @unlink(static::$container->getParameter('debug.container.dump')); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'debug:container', '--deprecations' => true]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertContains('Symfony\Bundle\FrameworkBundle\Controller\Controller', $tester->getDisplay()); + $this->assertContains('/home/hamza/projet/contrib/sf/vendor/symfony/framework-bundle/Controller/Controller.php', $tester->getDisplay()); + } + + public function testGetDeprecationNone() + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); + $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.cache_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + touch($path); + file_put_contents($path, serialize([])); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + @unlink(static::$container->getParameter('debug.container.dump')); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'debug:container', '--deprecations' => true]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertContains('[OK] There are no deprecations in the logs!', $tester->getDisplay()); + } + + public function testGetDeprecationNoFile(): void + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); + $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.cache_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + @unlink($path); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + @unlink(static::$container->getParameter('debug.container.dump')); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'debug:container', '--deprecations' => true]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertContains('[WARNING] The deprecation file does not exist', $tester->getDisplay()); + } + + public function testGetDeprecationXml(): void + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + @unlink(static::$container->getParameter('debug.container.dump')); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'debug:container', '--deprecations' => true, '--format' => 'xml']); + + $this->assertSame(1, $tester->getStatusCode()); + $this->assertContains('Using the XML format to print the deprecations is not supported.', $tester->getDisplay()); + } + public function provideIgnoreBackslashWhenFindingService() { return [