diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php index f520ab11f009..d5e6cb6c4acb 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php @@ -34,11 +34,22 @@ public function process(ContainerBuilder $container) // For instance, global variable definitions must be registered // afterward. If not, the globals from the extensions will never // be registered. - $calls = $definition->getMethodCalls(); - $definition->setMethodCalls(array()); + $currentMethodCalls = $definition->getMethodCalls(); + $twigBridgeExtensionsMethodCalls = array(); + $othersExtensionsMethodCalls = array(); foreach ($container->findTaggedServiceIds('twig.extension', true) as $id => $attributes) { - $definition->addMethodCall('addExtension', array(new Reference($id))); + $methodCall = array('addExtension', array(new Reference($id))); + $extensionClass = $container->getDefinition($id)->getClass(); + + if (is_string($extensionClass) && 0 === strpos($extensionClass, 'Symfony\Bridge\Twig\Extension')) { + $twigBridgeExtensionsMethodCalls[] = $methodCall; + } else { + $othersExtensionsMethodCalls[] = $methodCall; + } + } + + if (!empty($twigBridgeExtensionsMethodCalls) || !empty($othersExtensionsMethodCalls)) { + $definition->setMethodCalls(array_merge($twigBridgeExtensionsMethodCalls, $othersExtensionsMethodCalls, $currentMethodCalls)); } - $definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls)); } } diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php new file mode 100644 index 000000000000..b8fdf1af6afe --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class TwigEnvironmentPassTest extends TestCase +{ + public function testTwigBridgeExtensionsAreRegisteredFirst() + { + $twigDefinition = new Definition('twig'); + + $containerBuilderMock = $this->getMockBuilder(ContainerBuilder::class) + ->setMethods(array('hasDefinition', 'get', 'findTaggedServiceIds', 'getDefinition')) + ->getMock(); + $containerBuilderMock + ->expects($this->once()) + ->method('hasDefinition') + ->with('twig') + ->will($this->returnValue(true)); + $containerBuilderMock + ->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('twig.extension') + ->will($this->returnValue(array( + 'other_extension' => array( + array() + ), + 'twig_bridge_extension' => array( + array() + ) + ))); + + $otherExtensionDefinitionMock = $this->getMockBuilder(Definition::class) + ->setMethods(array('getClass')) + ->getMock(); + $otherExtensionDefinitionMock + ->expects($this->once()) + ->method('getClass') + ->will($this->returnValue('Foo\\Bar')); + + $twigExtensionDefinitionMock = $this->getMockBuilder(Definition::class) + ->setMethods(array('getClass')) + ->getMock(); + $twigExtensionDefinitionMock + ->expects($this->once()) + ->method('getClass') + ->will($this->returnValue('Symfony\\Bridge\\Twig\\Extension\\Foo')); + + $containerBuilderMock + ->expects($this->exactly(3)) + ->method('getDefinition') + ->withConsecutive(array('twig'), array('other_extension'), array('twig_bridge_extension')) + ->willReturnOnConsecutiveCalls( + $this->returnValue($twigDefinition), + $this->returnValue($otherExtensionDefinitionMock), + $this->returnValue($twigExtensionDefinitionMock) + ); + + $twigEnvironmentPass = new TwigEnvironmentPass(); + $twigEnvironmentPass->process($containerBuilderMock); + + $methodCalls = $twigDefinition->getMethodCalls(); + $this->assertCount(2, $methodCalls); + + $twigBridgeExtensionReference = $methodCalls[0][1][0]; + $this->assertInstanceOf(Reference::class, $twigBridgeExtensionReference); + /* @var Reference $twigBridgeExtensionReference */ + $this->assertEquals('twig_bridge_extension', $twigBridgeExtensionReference->__toString()); + + $otherExtensionReference = $methodCalls[1][1][0]; + $this->assertInstanceOf(Reference::class, $otherExtensionReference); + /* @var Reference $otherExtensionReference */ + $this->assertEquals('other_extension', $otherExtensionReference->__toString()); + } +}