feature #23519 [TwigBundle] Commands as a service (ro0NL)

This PR was squashed before being merged into the 3.4 branch (closes #23519).

Discussion
----------

[TwigBundle] Commands as a service

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

tiny step towards #23488

Commits
-------

98391402d1 [TwigBundle] Commands as a service
This commit is contained in:
Fabien Potencier 2017-07-22 09:51:14 +02:00
commit 68d9df6116
11 changed files with 156 additions and 34 deletions

View File

@ -83,6 +83,21 @@ TwigBridge
* deprecated the `Symfony\Bridge\Twig\Form\TwigRenderer` class, use the `FormRenderer`
class from the Form component instead
* deprecated `Symfony\Bridge\Twig\Command\DebugCommand::set/getTwigEnvironment` and the ability
to pass a command name as first argument
* deprecated `Symfony\Bridge\Twig\Command\LintCommand::set/getTwigEnvironment` and the ability
to pass a command name as first argument
TwigBundle
----------
* deprecated the `Symfony\Bundle\TwigBundle\Command\DebugCommand` class, use the `DebugCommand`
class from the Twig bridge instead
* deprecated relying on the `ContainerAwareInterface` implementation for
`Symfony\Bundle\TwigBundle\Command\LintCommand`
Validator
---------

View File

@ -511,6 +511,10 @@ TwigBundle
* The `ContainerAwareRuntimeLoader` class has been removed. Use the
Twig `Twig_ContainerRuntimeLoader` class instead.
* Removed `DebugCommand` in favor of `Symfony\Bridge\Twig\Command\DebugCommand`.
* Removed `ContainerAwareInterface` implementation in `Symfony\Bundle\TwigBundle\Command\LintCommand`.
TwigBridge
----------
@ -550,6 +554,12 @@ TwigBridge
* The `TwigRendererEngine::setEnvironment()` method has been removed.
Pass the Twig Environment as second argument of the constructor instead.
* Removed `Symfony\Bridge\Twig\Command\DebugCommand::set/getTwigEnvironment` and the ability
to pass a command name as first argument.
* Removed `Symfony\Bridge\Twig\Command\LintCommand::set/getTwigEnvironment` and the ability
to pass a command name as first argument.
Validator
---------

View File

@ -5,6 +5,8 @@ CHANGELOG
-----
* deprecated `Symfony\Bridge\Twig\Form\TwigRenderer`
* deprecated `Symfony\Bridge\Twig\Command\DebugCommand::set/getTwigEnvironment` and the ability to pass a command name as first argument
* deprecated `Symfony\Bridge\Twig\Command\LintCommand::set/getTwigEnvironment` and the ability to pass a command name as first argument
3.3.0
-----

View File

@ -29,15 +29,27 @@ class DebugCommand extends Command
private $twig;
/**
* {@inheritdoc}
* @param Environment $twig
*/
public function __construct($name = 'debug:twig')
public function __construct($twig = null)
{
parent::__construct($name);
parent::__construct();
if (!$twig instanceof Environment) {
@trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since version 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED);
$this->setName(null === $twig ? 'debug:twig' : $twig);
return;
}
$this->twig = $twig;
}
public function setTwigEnvironment(Environment $twig)
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
$this->twig = $twig;
}
@ -46,12 +58,15 @@ class DebugCommand extends Command
*/
protected function getTwigEnvironment()
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
return $this->twig;
}
protected function configure()
{
$this
->setName('debug:twig')
->setDefinition(array(
new InputArgument('filter', InputArgument::OPTIONAL, 'Show details for all entries matching this filter'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (text or json)', 'text'),
@ -80,9 +95,17 @@ EOF
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$twig = $this->getTwigEnvironment();
if (null === $twig) {
// BC to be removed in 4.0
if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, 'getTwigEnvironment');
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Usage of method "%s" is deprecated since version 3.4 and will no longer be supported in 4.0.', get_class($this).'::getTwigEnvironment'), E_USER_DEPRECATED);
$this->twig = $this->getTwigEnvironment();
}
}
if (null === $this->twig) {
throw new \RuntimeException('The Twig environment needs to be set.');
}
@ -91,7 +114,7 @@ EOF
if ($input->getOption('format') === 'json') {
$data = array();
foreach ($types as $type) {
foreach ($twig->{'get'.ucfirst($type)}() as $name => $entity) {
foreach ($this->twig->{'get'.ucfirst($type)}() as $name => $entity) {
$data[$type][$name] = $this->getMetadata($type, $entity);
}
}
@ -105,7 +128,7 @@ EOF
foreach ($types as $index => $type) {
$items = array();
foreach ($twig->{'get'.ucfirst($type)}() as $name => $entity) {
foreach ($this->twig->{'get'.ucfirst($type)}() as $name => $entity) {
if (!$filter || false !== strpos($name, $filter)) {
$items[$name] = $name.$this->getPrettyMetadata($type, $entity);
}

View File

@ -34,15 +34,27 @@ class LintCommand extends Command
private $twig;
/**
* {@inheritdoc}
* @param Environment $twig
*/
public function __construct($name = 'lint:twig')
public function __construct($twig = null)
{
parent::__construct($name);
parent::__construct();
if (!$twig instanceof Environment) {
@trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since version 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED);
$this->setName(null === $twig ? 'lint:twig' : $twig);
return;
}
$this->twig = $twig;
}
public function setTwigEnvironment(Environment $twig)
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
$this->twig = $twig;
}
@ -51,12 +63,15 @@ class LintCommand extends Command
*/
protected function getTwigEnvironment()
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
return $this->twig;
}
protected function configure()
{
$this
->setName('lint:twig')
->setDescription('Lints a template and outputs encountered errors')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addArgument('filename', InputArgument::IS_ARRAY)
@ -86,7 +101,16 @@ EOF
{
$io = new SymfonyStyle($input, $output);
if (null === $twig = $this->getTwigEnvironment()) {
// BC to be removed in 4.0
if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, 'getTwigEnvironment');
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Usage of method "%s" is deprecated since version 3.4 and will no longer be supported in 4.0.', get_class($this).'::getTwigEnvironment'), E_USER_DEPRECATED);
$this->twig = $this->getTwigEnvironment();
}
}
if (null === $this->twig) {
throw new \RuntimeException('The Twig environment needs to be set.');
}
@ -102,20 +126,20 @@ EOF
$template .= fread(STDIN, 1024);
}
return $this->display($input, $output, $io, array($this->validate($twig, $template, uniqid('sf_', true))));
return $this->display($input, $output, $io, array($this->validate($template, uniqid('sf_', true))));
}
$filesInfo = $this->getFilesInfo($twig, $filenames);
$filesInfo = $this->getFilesInfo($filenames);
return $this->display($input, $output, $io, $filesInfo);
}
private function getFilesInfo(Environment $twig, array $filenames)
private function getFilesInfo(array $filenames)
{
$filesInfo = array();
foreach ($filenames as $filename) {
foreach ($this->findFiles($filename) as $file) {
$filesInfo[] = $this->validate($twig, file_get_contents($file), $file);
$filesInfo[] = $this->validate(file_get_contents($file), $file);
}
}
@ -133,17 +157,17 @@ EOF
throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename));
}
private function validate(Environment $twig, $template, $file)
private function validate($template, $file)
{
$realLoader = $twig->getLoader();
$realLoader = $this->twig->getLoader();
try {
$temporaryLoader = new ArrayLoader(array((string) $file => $template));
$twig->setLoader($temporaryLoader);
$nodeTree = $twig->parse($twig->tokenize(new Source($template, (string) $file)));
$twig->compile($nodeTree);
$twig->setLoader($realLoader);
$this->twig->setLoader($temporaryLoader);
$nodeTree = $this->twig->parse($this->twig->tokenize(new Source($template, (string) $file)));
$this->twig->compile($nodeTree);
$this->twig->setLoader($realLoader);
} catch (Error $e) {
$twig->setLoader($realLoader);
$this->twig->setLoader($realLoader);
return array('template' => $template, 'file' => $file, 'line' => $e->getTemplateLine(), 'valid' => false, 'exception' => $e);
}

View File

@ -68,15 +68,30 @@ class LintCommandTest extends TestCase
$this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay()));
}
/**
* @group legacy
* @expectedDeprecation Passing a command name as the first argument of "Symfony\Bridge\Twig\Command\LintCommand::__construct" is deprecated since version 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.
* @expectedException \RuntimeException
* @expectedExceptionMessage The Twig environment needs to be set.
*/
public function testLegacyLintCommand()
{
$command = new LintCommand();
$application = new Application();
$application->add($command);
$command = $application->find('lint:twig');
$tester = new CommandTester($command);
$tester->execute(array());
}
/**
* @return CommandTester
*/
private function createCommandTester()
{
$twig = new Environment(new FilesystemLoader());
$command = new LintCommand();
$command->setTwigEnvironment($twig);
$command = new LintCommand(new Environment(new FilesystemLoader()));
$application = new Application();
$application->add($command);

View File

@ -1,6 +1,12 @@
CHANGELOG
=========
3.4.0
-----
* deprecated `Symfony\Bundle\TwigBundle\Command\DebugCommand`, use `Symfony\Bridge\Twig\Command\DebugCommand` instead
* deprecated relying on the `ContainerAwareInterface` implementation for `Symfony\Bundle\TwigBundle\Command\LintCommand`
3.3.0
-----

View File

@ -11,6 +11,8 @@
namespace Symfony\Bundle\TwigBundle\Command;
@trigger_error(sprintf('The %s class is deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Bridge\Twig\Command\DebugCommand instead.', DebugCommand::class), E_USER_DEPRECATED);
use Symfony\Bridge\Twig\Command\DebugCommand as BaseDebugCommand;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
@ -19,6 +21,8 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait;
* Lists twig functions, filters, globals and tests present in the current project.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @deprecated since version 3.4, to be removed in 4.0.
*/
final class DebugCommand extends BaseDebugCommand implements ContainerAwareInterface
{

View File

@ -24,16 +24,9 @@ use Symfony\Component\Finder\Finder;
*/
final class LintCommand extends BaseLintCommand implements ContainerAwareInterface
{
// BC to be removed in 4.0
use ContainerAwareTrait;
/**
* {@inheritdoc}
*/
protected function getTwigEnvironment()
{
return $this->container->get('twig');
}
/**
* {@inheritdoc}
*/

View File

@ -13,6 +13,7 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection;
use Symfony\Bridge\Twig\Extension\WebLinkExtension;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@ -49,6 +50,10 @@ class TwigExtension extends Extension
$loader->load('templating.xml');
}
if (class_exists(Application::class)) {
$loader->load('console.xml');
}
if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) {
$container->removeDefinition('twig.translation.extractor');
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<defaults public="false" />
<service id="twig.command.debug" class="Symfony\Bridge\Twig\Command\DebugCommand">
<argument type="service" id="twig" />
<tag name="console.command" command="debug:twig" />
</service>
<service id="twig.command.lint" class="Symfony\Bundle\TwigBundle\Command\LintCommand">
<argument type="service" id="twig" />
<tag name="console.command" command="lint:twig" />
</service>
<!-- BC to be removed in 4.0 -->
<service id="console.command.symfony_bundle_twigbundle_command_debugcommand" class="Symfony\Bundle\TwigBundle\Command\DebugCommand" public="true">
<deprecated>The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use "twig.command.debug" instead.</deprecated>
</service>
</services>
</container>