8000 Deprecate some interface in Twig Bridge by fabpot · Pull Request #16333 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

Deprecate some interface in Twig Bridge #16333

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

Closed
wants to merge 3 commits into from
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/Symfony/Bridge/Twig/Extension/ContainerAwareRuntimeLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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\Bridge\Twig\Extension;

use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Loads Twig extension runtimes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ContainerAwareRuntimeLoader implements \Twig_RuntimeLoaderInterface
{
private $container;

public function __construct(ContainerInterface $container)
{
$this->container = $container;
}

/**
* {@inheritdoc}
*/
public function load($name)
{
$id = 'twig.extension.runtime.'.$name;
Copy link
Member

Choose a reason for hiding this comment

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

please don't force this convention: it forbids third-party bundles to use this behavior while following best practices for shared bundles. The id of the runtime should be provided as an optional attribute in the twig.extension tag IMO, to build a mapping between extension names and runtimes

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, this is just a quick POC to prove that it works.


if ($this->container->has($id)) {
return $this->container->get($id);
}
}
}
76 changes: 1 addition & 75 deletions src/Symfony/Bridge/Twig/Extension/FormExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Symfony\Bridge\Twig\Extension;

use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
use Symfony\Bridge\Twig\Form\TwigRendererInterface;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;

/**
Expand All @@ -21,29 +20,8 @@
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormExtension extends \Twig_Extension implements \Twig_Extension_InitRuntimeInterface
class FormExtension extends \Twig_Extension
{
/**
* This property is public so that it can be accessed directly from compiled
* templates without having to call a getter, which slightly decreases performance.
*
* @var TwigRendererInterface
*/
public $renderer;

public function __construct(TwigRendererInterface $renderer)
{
$this->renderer = $renderer;
}

/**
* {@inheritdoc}
*/
public function initRuntime(\Twig_Environment $environment)
{
$this->renderer->setEnvironment($environment);
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -93,58 +71,6 @@ public function getTests()
);
}

/**
* {@inheritdoc}
*/
public function renderCsrfToken($tokenId)
{
return $this->renderer->renderCsrfToken($tokenId);
}

/**
* Makes a technical name human readable.
*
* @param string $text The text to humanize
*
* @return string The humanized text
*/
public function humanize($text)
{
return $this->renderer->humanize($text);
}

/**
* Returns whether a choice is selected for a given form value.
*
* Unfortunately Twig does not support an efficient way to execute the
* "is_selected" closure passed to the template by ChoiceType. It is faster
* to implement the logic here (around 65ms for a specific form).
*
* Directly implementing the logic here is also faster than doing so in
* ChoiceView (around 30ms).
*
* The worst option tested so far is to implement the logic in ChoiceView
* and access the ChoiceView method directly in the template. Doing so is
* around 220ms slower than doing the method call here in the filter. Twig
* seems to be much more efficient at executing filters than at executing
* methods of an object.
*
* @param ChoiceView $choice The choice to check
* @param string|array $selectedValue The selected value to compare
*
* @return bool Whether the choice is selected
*
* @see ChoiceView::isSelected()
*/
public function isSelectedChoice(ChoiceView $choice, $selectedValue)
{
if (is_array($selectedValue)) {
return in_array($choice->value, $selectedValue, true);
}

return $choice->value === $selectedValue;
}

/**
* {@inheritdoc}
*/
Expand Down
45 changes: 34 additions & 11 deletions src/Symfony/Bridge/Twig/Form/TwigRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,53 @@
namespace Symfony\Bridge\Twig\Form;

use Symfony\Component\Form\FormRenderer;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;

/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class TwigRenderer extends FormRenderer implements TwigRendererInterface
{
/**
* @var TwigRendererEngineInterface
* {@inheritdoc}
*
* @deprecated Deprecated in 2.8, to be removed in 3.0.
*/
private $engine;

public function __construct(TwigRendererEngineInterface $engine, CsrfTokenManagerInterface $csrfTokenManager = null)
public function setEnvironment(\Twig_Environment $environment)
{
parent::__construct($engine, $csrfTokenManager);

$this->engine = $engine;
}

/**
* {@inheritdoc}
* Returns whether a choice is selected for a given form value.
*
* Unfortunately Twig does not support an efficient way to execute the
* "is_selected" closure passed to the template by ChoiceType. It is faster
* to implement the logic here (around 65ms for a specific form).
*
* Directly implementing the logic here is also faster than doing so in
* ChoiceView (around 30ms).
*
* The worst option tested so far is to implement the logic in ChoiceView
* and access the ChoiceView method directly in the template. Doing so is
* around 220ms slower than doing the method call here in the filter. Twig
* seems to be much more efficient at executing filters than at executing
* methods of an object.
*
* @param ChoiceView $choice The choice to check.
* @param string|array $selectedValue The selected value to compare.
*
* @return bool Whether the choice is selected.
*
* @see ChoiceView::isSelected()
*/
public function setEnvironment(\Twig_Environment $environment)
public function isSelectedChoice(ChoiceView $choice, $selectedValue)
{
$this->engine->setEnvironment($environment);
if (is_array($selectedValue)) {
return in_array($choice->value, $selectedValue, true);
}

return $choice->value === $selectedValue;
}
}
12 changes: 11 additions & 1 deletion src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererEngineInterface
{
Expand All @@ -29,12 +31,20 @@ class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererE
*/
private $template;

public function __construct(array $defaultThemes = array(), \Twig_Environment $environment)
Copy link
Member

Choose a reason for hiding this comment

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

this is a BC break as the 2 arguments are mandatory

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't you think that this class is internal?

Copy link
Member

Choose a reason for hiding this comment

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

people using it have to instantiate it explicitly. This signature change would break Silex for instance

{
parent::__construct($defaultThemes);

$this->environment = $environment;
}

/**
* {@inheritdoc}
*
* @deprecated Deprecated in 2.8, to be removed in 3.0.
Copy link
Member

Choose a reason for hiding this comment

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

deprecation warnings should be added too

*/
public function setEnvironment(\Twig_Environment $environment)
{
$this->environment = $environment;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated Deprecated in 2.8, to be removed in 3.0.
*
* @internal
*/
interface TwigRendererEngineInterface extends FormRendererEngineInterface
{
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated Deprecated in 2.8, to be removed in 3.0.
*
* @internal
*/
interface TwigRendererInterface extends FormRendererInterface
{
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/Twig/Node/FormThemeNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function compile(\Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('$this->env->getExtension(\'Symfony\Bridge\Twig\Extension\FormExtension\')->renderer->setTheme(')
->write('$this->env->getRuntime(\'Symfony\Bridge\Twig\Extension\FormExtension\')->setTheme(')
->subcompile($this->getNode('form'))
->raw(', ')
->subcompile($this->getNode('resources'))
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/Twig/Node/RenderBlockNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$arguments = iterator_to_array($this->getNode('arguments'));
$compiler->write('$this->env->getExtension(\'Symfony\Bridge\Twig\Extension\FormExtension\')->renderer->renderBlock(');
$compiler->write('$this->env->getRuntime(\'Symfony\Bridge\Twig\Extension\FormExtension\')->renderBlock(');

if (isset($arguments[0])) {
$compiler->subcompile($arguments[0]);
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class SearchAndRenderBlockNode extends \Twig_Node_Expression_Function
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$compiler->raw('$this->env->getExtension(\'Symfony\Bridge\Twig\Extension\FormExtension\')->renderer->searchAndRenderBlock(');
$compiler->raw('$this->env->getRuntime(\'Symfony\Bridge\Twig\Extension\FormExtension\')->searchAndRenderBlock(');

preg_match('/_([^_]+)$/', $this->getAttribute('name'), $matches);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,37 @@

class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest
{
/**
* @var FormExtension
*/
protected $extension;

protected $testableFeatures = array(
'choice_attr',
);

private $environment;

protected function setUp()
{
parent::setUp();

$rendererEngine = new TwigRendererEngine(array(
'bootstrap_3_horizontal_layout.html.twig',
'custom_widgets.html.twig',
));
$renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'));

$this->extension = new FormExtension($renderer);

$loader = new StubFilesystemLoader(array(
__DIR__.'/../../Resources/views/Form',
__DIR__.'/Fixtures/templates/form',
));

$environment = new \Twig_Environment($loader, array('strict_variables' => true));
$environment->addExtension(new TranslationExtension(new StubTranslator()));
$environment->addExtension($this->extension);
$this->environment = new \Twig_Environment($loader, array('strict_variables' => true));
$this->environment->addExtension(new TranslationExtension(new StubTranslator()));
$this->environment->addExtension(new FormExtension());

$rendererEngine = new TwigRendererEngine(array(
'bootstrap_3_horizontal_layout.html.twig',
'custom_widgets.html.twig',
), $this->environment);
$renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'));

$this->extension->initRuntime($environment);
$loader = $this->getMock('Twig_RuntimeLoaderInterface');
$loader->expects($this->any())->method('load')->will($this->returnValueMap(array(
array('form', $renderer),
array('translator', null),
)));
$this->environment->registerRuntimeLoader($loader);
}

protected function tearDown()
Expand All @@ -64,7 +64,7 @@ protected function tearDown()

protected function renderForm(FormView $view, array $vars = array())
{
return (string) $this->extension->renderer->renderBlock($view, 'form', $vars);
return (string) $this->environment->getRuntime('form')->renderBlock($view, 'form', $vars);
}

protected function renderLabel(FormView $view, $label = null, array $vars = array())
Expand All @@ -73,41 +73,41 @@ protected function renderLabel(FormView $view, $label = null, array $vars = arra
$vars += array('label' => $label);
}

return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars);
return (string) $this->environment->getRuntime('form')->searchAndRenderBlock($view, 'label', $vars);
}

protected function renderErrors(FormView $view)
{
return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors');
return (string) $this->environment->getRuntime('form')->searchAndRenderBlock($view, 'errors');
}

protected function renderWidget(FormView $view, array $vars = array())
{
return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars);
return (string) $this->environment->getRuntime('form')->searchAndRenderBlock($view, 'widget', $vars);
}

protected function renderRow(FormView $view, array $vars = array())
{
return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars);
return (string) $this->environment->getRuntime('form')->searchAndRenderBlock($view, 'row', $vars);
}

protected function renderRest(FormView $view, array $vars = array())
{
return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars);
return (string) $this->environment->getRuntime('form')->searchAndRenderBlock($view, 'rest', $vars);
}

protected function renderStart(FormView $view, array $vars = array())
{
return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars);
return (string) $this->environment->getRuntime('form')->renderBlock($view, 'form_start', $vars);
}

protected function renderEnd(FormView $view, array $vars = array())
{
return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars);
return (string) $this->environment->getRuntime('form')->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
{
$this->extension->renderer->setTheme($view, $themes);
$this->environment->getRuntime('form')->setTheme($view, $themes);
}
}
Loading
0