-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
added a Twig runtime loader #20092
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
added a Twig runtime loader #20092
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?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\TwigBundle; | ||
|
||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
/** | ||
* Loads Twig extension runtimes via the service container. | ||
* | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
*/ | ||
class ContainerAwareRuntimeLoader implements \Twig_RuntimeLoaderInterface | ||
{ | ||
private $container; | ||
private $mapping; | ||
|
||
public function __construct(ContainerInterface $container, array $mapping) | ||
{ | ||
$this->container = $container; | ||
$this->mapping = $mapping; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function load($class) | ||
{ | ||
if (!isset($this->mapping[$class])) { | ||
throw new \LogicException(sprintf('Class "%s" is not configured as a Twig runtime. Add the "twig.runtime" tag to the related service in the container.', $class)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't it return null instead, to play well with multiple runtime loaders, as supported by Twig ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that but I don't think people will register some other runtime loaders when using Symfony. And returning null here means that debugging an issue would be very difficult. The other possibility would be for Twig to catch exceptions and keep it for when no loaders work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other possibility is to inject a logger and log such errors. WDYT? I don't like catching the exceptions in Twig as we try to avoid that as much as possible (mainly for performance reasons). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with logging |
||
} | ||
|
||
return $this->container->get($this->mapping[$class]); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?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\TwigBundle\DependencyInjection\Compiler; | ||
|
||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
|
||
/** | ||
* Registers Twig runtime services. | ||
*/ | ||
class RuntimeLoaderPass implements CompilerPassInterface | ||
{ | ||
public function process(ContainerBuilder $container) | ||
{ | ||
if (!$container->hasDefinition('twig.runtime_loader')) { | ||
return; | ||
} | ||
|
||
$definition = $container->getDefinition('twig.runtime_loader'); | ||
$mapping = array(); | ||
foreach ($container->findTaggedServiceIds('twig.runtime') as $id => $attributes) { | ||
$mapping[$container->getDefinition($id)->getClass()] = $id; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should validate that services are public (and not abstract), to provide an early error message for such mistake There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see #20112 |
||
} | ||
|
||
$definition->replaceArgument(1, $mapping); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,9 @@ | |
<argument>app</argument> | ||
<argument type="service" id="twig.app_variable" /> | ||
</call> | ||
<call method="addRuntimeLoader"> | ||
<argument type="service" id="twig.runtime_loader" /> | ||
</call> | ||
<configurator service="twig.configurator.environment" method="configure" /> | ||
</service> | ||
|
||
|
@@ -128,6 +131,7 @@ | |
<service id="twig.form.renderer" class="Symfony\Bridge\Twig\Form\TwigRenderer" public="false"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. must be public to be lazy-loadable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indeed, fixed |
||
<argument type="service" id="twig.form.engine" /> | ||
<argument type="service" id="security.csrf.token_manager" on-invalid="null" /> | ||
<tag name="twig.runtime" /> | ||
</service> | ||
|
||
<service id="twig.translation.extractor" class="Symfony\Bridge\Twig\Translation\TwigExtractor"> | ||
|
@@ -160,5 +164,10 @@ | |
<argument /> <!-- decimal point, set in TwigExtension --> | ||
<argument /> <!-- thousands separator, set in TwigExtension --> | ||
</service> | ||
|
||
<service id="twig.runtime_loader" class="Symfony\Bundle\TwigBundle\ContainerAwareRuntimeLoader" public="false"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="collection" /> <!-- the mapping between class names and service names --> | ||
</service> | ||
</services> | ||
</container> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?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\TwigBundle\Tests; | ||
|
||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
use Symfony\Bundle\TwigBundle\ContainerAwareRuntimeLoader; | ||
|
||
class ContainerAwareRuntimeLoaderTest extends TestCase | ||
{ | ||
public function testLoad() | ||
{ | ||
$container = $this->getMockBuilder(ContainerInterface::class)->getMock(); | ||
$container->expects($this->once())->method('get')->with('foo'); | ||
|
||
$loader = new ContainerAwareRuntimeLoader($container, array( | ||
'FooClass' => 'foo', | ||
)); | ||
$loader->load('FooClass'); | ||
} | ||
|
||
/** | ||
* @expectedException \LogicException | ||
* @expectedExceptionMessage Class "BarClass" is not configured as a Twig runtime. | ||
*/ | ||
public function testLoadWithoutAMatch() | ||
{ | ||
$loader = new ContainerAwareRuntimeLoader($this->getMockBuilder(ContainerInterface::class)->getMock(), array()); | ||
$loader->load('BarClass'); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Injecting a container but not actually assigning it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, added some tests as well :)