Skip to content

Commit

Permalink
feature #13761 Automatically process extensions when they implement C…
Browse files Browse the repository at this point in the history
…ompilerPassInterface (WouterJ)

This PR was merged into the 2.7 branch.

Discussion
----------

Automatically process extensions when they implement CompilerPassInterface

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | todo (if people are in favor of this PR)

Compiler passes are very powerfull, but also quite strange to work with. Especially when you just need a very simple compiler pass (like https://github.com/symfony-cmf/RoutingBundle/blob/master/DependencyInjection/Compiler/SetRouterPass.php). For 3 lines of code, you need to tweak your bundle class and create a new class.

When using the DI component standalone, compiler passes are even harder to work with, as DI extensions can't register them. I believe that's why libraries like Behat make their extensions compiler passes by default.

I think it would be very easy to just implement an interface and have a `compile` method for the simple compiler pass stuff. If a bundle needs multiple compiler passes or need compiler passes to be executed at other times in the compile process, a bundle can use the normal compiler passes. But if it's just one simple thing, like replacing a definition or getting services with a specific tag, I think this method will be very usefull.

Commits
-------

6c50013 Allowed extensions to inline compiler passes
  • Loading branch information
fabpot committed Sep 15, 2015
2 parents c38326c + 6c50013 commit 1abfaba
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
@@ -0,0 +1,28 @@
<?php

namespace Symfony\Component\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* A pass to automatically process extensions if they implement
* CompilerPassInterface.
*
* @author Wouter J <wouter@wouterj.nl>
*/
class ExtensionCompilerPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
foreach ($container->getExtensions() as $extension) {
if (!$extension instanceof CompilerPassInterface) {
continue;
}

$extension->process($container);
}
}
}
Expand Up @@ -45,6 +45,7 @@ public function __construct()
$this->mergePass = new MergeExtensionConfigurationPass();

$this->optimizationPasses = array(
new ExtensionCompilerPass(),
new ResolveDefinitionTemplatesPass(),
new DecoratorServicePass(),
new ResolveParameterPlaceHoldersPass(),
Expand Down
@@ -0,0 +1,46 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Compiler;

use Symfony\Component\DependencyInjection\Compiler\ExtensionCompilerPass;

/**
* @author Wouter J <wouter@wouterj.nl>
*/
class ExtensionCompilerPassTest extends \PHPUnit_Framework_TestCase
{
private $container;
private $pass;

public function setUp()
{
$this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
$this->pass = new ExtensionCompilerPass();
}

public function testProcess()
{
$extension1 = $this->createExtensionMock(true);
$extension1->expects($this->once())->method('process');
$extension2 = $this->createExtensionMock(false);
$extension3 = $this->createExtensionMock(false);
$extension4 = $this->createExtensionMock(true);
$extension4->expects($this->once())->method('process');

$this->container->expects($this->any())
->method('getExtensions')
->will($this->returnValue(array($extension1, $extension2, $extension3, $extension4)))
;

$this->pass->process($this->container);
}

private function createExtensionMock($hasInlineCompile)
{
return $this->getMock('Symfony\Component\DependencyInjection\\'.(
$hasInlineCompile
? 'Compiler\CompilerPassInterface'
: 'Extension\ExtensionInterface'
));
}
}

0 comments on commit 1abfaba

Please sign in to comment.