[DependencyInjection] Sort the CompilerPass by priority

This commit is contained in:
Ener-Getick 2016-03-05 13:39:12 +01:00 committed by Fabien Potencier
parent fce02990f2
commit d17c1a9734
6 changed files with 138 additions and 46 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
3.2.0
-----
* allowed to prioritize compiler passes by introducing a third argument to `PassConfig::addPass()`, to `Compiler::addPass` and to `ContainerBuilder::addCompilerPass()`
3.0.0 3.0.0
----- -----

View File

@ -65,12 +65,20 @@ class Compiler
/** /**
* Adds a pass to the PassConfig. * Adds a pass to the PassConfig.
* *
* @param CompilerPassInterface $pass A compiler pass * @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of the pass * @param string $type The type of the pass
* @param int $priority Used to sort the passes
*/ */
public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION) public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/**, $priority = 0*/)
{ {
$this->passConfig->addPass($pass, $type); // For BC
if (func_num_args() >= 3) {
$priority = func_get_arg(2);
} else {
$priority = 0;
}
$this->passConfig->addPass($pass, $type, $priority);
} }
/** /**

View File

@ -39,7 +39,7 @@ class PassConfig
{ {
$this->mergePass = new MergeExtensionConfigurationPass(); $this->mergePass = new MergeExtensionConfigurationPass();
$this->optimizationPasses = array( $this->optimizationPasses = array(array(
new ExtensionCompilerPass(), new ExtensionCompilerPass(),
new ResolveDefinitionTemplatesPass(), new ResolveDefinitionTemplatesPass(),
new DecoratorServicePass(), new DecoratorServicePass(),
@ -51,9 +51,9 @@ class PassConfig
new AnalyzeServiceReferencesPass(true), new AnalyzeServiceReferencesPass(true),
new CheckCircularReferencesPass(), new CheckCircularReferencesPass(),
new CheckReferenceValidityPass(), new CheckReferenceValidityPass(),
); ));
$this->removingPasses = array( $this->removingPasses = array(array(
new RemovePrivateAliasesPass(), new RemovePrivateAliasesPass(),
new ReplaceAliasByActualDefinitionPass(), new ReplaceAliasByActualDefinitionPass(),
new RemoveAbstractDefinitionsPass(), new RemoveAbstractDefinitionsPass(),
@ -64,98 +64,111 @@ class PassConfig
new RemoveUnusedDefinitionsPass(), new RemoveUnusedDefinitionsPass(),
)), )),
new CheckExceptionOnInvalidReferenceBehaviorPass(), new CheckExceptionOnInvalidReferenceBehaviorPass(),
); ));
} }
/** /**
* Returns all passes in order to be processed. * Returns all passes in order to be processed.
* *
* @return array An array of all passes to process * @return CompilerPassInterface[]
*/ */
public function getPasses() public function getPasses()
{ {
return array_merge( return array_merge(
array($this->mergePass), array($this->mergePass),
$this->beforeOptimizationPasses, $this->getBeforeOptimizationPasses(),
$this->optimizationPasses, $this->getOptimizationPasses(),
$this->beforeRemovingPasses, $this->getBeforeRemovingPasses(),
$this->removingPasses, $this->getRemovingPasses(),
$this->afterRemovingPasses $this->getAfterRemovingPasses()
); );
} }
/** /**
* Adds a pass. * Adds a pass.
* *
* @param CompilerPassInterface $pass A Compiler pass * @param CompilerPassInterface $pass A Compiler pass
* @param string $type The pass type * @param string $type The pass type
* @param int $priority Used to sort the passes
* *
* @throws InvalidArgumentException when a pass type doesn't exist * @throws InvalidArgumentException when a pass type doesn't exist
*/ */
public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION) public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/)
{ {
// For BC
if (func_num_args() >= 3) {
$priority = func_get_arg(2);
} else {
$priority = 0;
}
$property = $type.'Passes'; $property = $type.'Passes';
if (!isset($this->$property)) { if (!isset($this->$property)) {
throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type));
} }
$this->{$property}[] = $pass; $passes = &$this->$property;
if (!isset($passes[$priority])) {
$passes[$priority] = array();
}
$passes[$priority][] = $pass;
} }
/** /**
* Gets all passes for the AfterRemoving pass. * Gets all passes for the AfterRemoving pass.
* *
* @return array An array of passes * @return CompilerPassInterface[]
*/ */
public function getAfterRemovingPasses() public function getAfterRemovingPasses()
{ {
return $this->afterRemovingPasses; return $this->sortPasses($this->afterRemovingPasses);
} }
/** /**
* Gets all passes for the BeforeOptimization pass. * Gets all passes for the BeforeOptimization pass.
* *
* @return array An array of passes * @return CompilerPassInterface[]
*/ */
public function getBeforeOptimizationPasses() public function getBeforeOptimizationPasses()
{ {
return $this->beforeOptimizationPasses; return $this->sortPasses($this->beforeOptimizationPasses);
} }
/** /**
* Gets all passes for the BeforeRemoving pass. * Gets all passes for the BeforeRemoving pass.
* *
* @return array An array of passes * @return CompilerPassInterface[]
*/ */
public function getBeforeRemovingPasses() public function getBeforeRemovingPasses()
{ {
return $this->beforeRemovingPasses; return $this->sortPasses($this->beforeRemovingPasses);
} }
/** /**
* Gets all passes for the Optimization pass. * Gets all passes for the Optimization pass.
* *
* @return array An array of passes * @return CompilerPassInterface[]
*/ */
public function getOptimizationPasses() public function getOptimizationPasses()
{ {
return $this->optimizationPasses; return $this->sortPasses($this->optimizationPasses);
} }
/** /**
* Gets all passes for the Removing pass. * Gets all passes for the Removing pass.
* *
* @return array An array of passes * @return CompilerPassInterface[]
*/ */
public function getRemovingPasses() public function getRemovingPasses()
{ {
return $this->removingPasses; return $this->sortPasses($this->removingPasses);
} }
/** /**
* Gets all passes for the Merge pass. * Gets all passes for the Merge pass.
* *
* @return array An array of passes * @return CompilerPassInterface
*/ */
public function getMergePass() public function getMergePass()
{ {
@ -175,50 +188,69 @@ class PassConfig
/** /**
* Sets the AfterRemoving passes. * Sets the AfterRemoving passes.
* *
* @param array $passes An array of passes * @param CompilerPassInterface[] $passes
*/ */
public function setAfterRemovingPasses(array $passes) public function setAfterRemovingPasses(array $passes)
{ {
$this->afterRemovingPasses = $passes; $this->afterRemovingPasses = array($passes);
} }
/** /**
* Sets the BeforeOptimization passes. * Sets the BeforeOptimization passes.
* *
* @param array $passes An array of passes * @param CompilerPassInterface[] $passes
*/ */
public function setBeforeOptimizationPasses(array $passes) public function setBeforeOptimizationPasses(array $passes)
{ {
$this->beforeOptimizationPasses = $passes; $this->beforeOptimizationPasses = array($passes);
} }
/** /**
* Sets the BeforeRemoving passes. * Sets the BeforeRemoving passes.
* *
* @param array $passes An array of passes * @param CompilerPassInterface[] $passes
*/ */
public function setBeforeRemovingPasses(array $passes) public function setBeforeRemovingPasses(array $passes)
{ {
$this->beforeRemovingPasses = $passes; $this->beforeRemovingPasses = array($passes);
} }
/** /**
* Sets the Optimization passes. * Sets the Optimization passes.
* *
* @param array $passes An array of passes * @param CompilerPassInterface[] $passes
*/ */
public function setOptimizationPasses(array $passes) public function setOptimizationPasses(array $passes)
{ {
$this->optimizationPasses = $passes; $this->optimizationPasses = array($passes);
} }
/** /**
* Sets the Removing passes. * Sets the Removing passes.
* *
* @param array $passes An array of passes * @param CompilerPassInterface[] $passes
*/ */
public function setRemovingPasses(array $passes) public function setRemovingPasses(array $passes)
{ {
$this->removingPasses = $passes; $this->removingPasses = array($passes);
}
/**
* Sort passes by priority.
*
* @param array $passes CompilerPassInterface instances with their priority as key.
*
* @return CompilerPassInterface[]
*/
private function sortPasses(array $passes)
{
if (0 === count($passes)) {
return array();
}
krsort($passes);
// Flatten the array
return call_user_func_array('array_merge', $passes);
} }
} }

View File

@ -298,14 +298,22 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
/** /**
* Adds a compiler pass. * Adds a compiler pass.
* *
* @param CompilerPassInterface $pass A compiler pass * @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of compiler pass * @param string $type The type of compiler pass
* @param int $priority Used to sort the passes
* *
* @return ContainerBuilder The current instance * @return ContainerBuilder The current instance
*/ */
public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION) public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/**, $priority = 0*/)
{ {
$this->getCompiler()->addPass($pass, $type); // For BC
if (func_num_args() >= 3) {
$priority = func_get_arg(2);
} else {
$priority = 0;
}
$this->getCompiler()->addPass($pass, $type, $priority);
$this->addObjectResource($pass); $this->addObjectResource($pass);

View File

@ -0,0 +1,34 @@
<?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\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* @author Guilhem N <egetick@gmail.com>
*/
class PassConfigTest extends \PHPUnit_Framework_TestCase
{
public function testPassOrdering()
{
$config = new PassConfig();
$pass1 = $this->getMock(CompilerPassInterface::class);
$config->addPass($pass1, PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
$pass2 = $this->getMock(CompilerPassInterface::class);
$config->addPass($pass2, PassConfig::TYPE_BEFORE_OPTIMIZATION, 30);
$this->assertSame(array($pass2, $pass1), $config->getBeforeOptimizationPasses());
}
}

View File

@ -17,6 +17,7 @@ require_once __DIR__.'/Fixtures/includes/ProjectExtension.php';
use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Bridge\PhpUnit\ErrorAssert;
use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
@ -284,10 +285,14 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{ {
$builder = new ContainerBuilder(); $builder = new ContainerBuilder();
$builder->setResourceTracking(false); $builder->setResourceTracking(false);
$builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses(); $defaultPasses = $builder->getCompiler()->getPassConfig()->getPasses();
$builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface')); $builder->addCompilerPass($pass1 = $this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'), PassConfig::TYPE_BEFORE_OPTIMIZATION, -5);
$builder->addCompilerPass($pass2 = $this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
$this->assertCount(count($builder->getCompiler()->getPassConfig()->getPasses()) - 1, $builderCompilerPasses); $passes = $builder->getCompiler()->getPassConfig()->getPasses();
$this->assertCount(count($passes) - 2, $defaultPasses);
// Pass 1 is executed later
$this->assertTrue(array_search($pass1, $passes, true) > array_search($pass2, $passes, true));
} }
public function testCreateService() public function testCreateService()