[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
=========
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
-----

View File

@ -65,12 +65,20 @@ class Compiler
/**
* Adds a pass to the PassConfig.
*
* @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of the pass
* @param CompilerPassInterface $pass A compiler 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->optimizationPasses = array(
$this->optimizationPasses = array(array(
new ExtensionCompilerPass(),
new ResolveDefinitionTemplatesPass(),
new DecoratorServicePass(),
@ -51,9 +51,9 @@ class PassConfig
new AnalyzeServiceReferencesPass(true),
new CheckCircularReferencesPass(),
new CheckReferenceValidityPass(),
);
));
$this->removingPasses = array(
$this->removingPasses = array(array(
new RemovePrivateAliasesPass(),
new ReplaceAliasByActualDefinitionPass(),
new RemoveAbstractDefinitionsPass(),
@ -64,98 +64,111 @@ class PassConfig
new RemoveUnusedDefinitionsPass(),
)),
new CheckExceptionOnInvalidReferenceBehaviorPass(),
);
));
}
/**
* Returns all passes in order to be processed.
*
* @return array An array of all passes to process
* @return CompilerPassInterface[]
*/
public function getPasses()
{
return array_merge(
array($this->mergePass),
$this->beforeOptimizationPasses,
$this->optimizationPasses,
$this->beforeRemovingPasses,
$this->removingPasses,
$this->afterRemovingPasses
$this->getBeforeOptimizationPasses(),
$this->getOptimizationPasses(),
$this->getBeforeRemovingPasses(),
$this->getRemovingPasses(),
$this->getAfterRemovingPasses()
);
}
/**
* Adds a pass.
*
* @param CompilerPassInterface $pass A Compiler pass
* @param string $type The pass type
* @param CompilerPassInterface $pass A Compiler pass
* @param string $type The pass type
* @param int $priority Used to sort the passes
*
* @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';
if (!isset($this->$property)) {
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.
*
* @return array An array of passes
* @return CompilerPassInterface[]
*/
public function getAfterRemovingPasses()
{
return $this->afterRemovingPasses;
return $this->sortPasses($this->afterRemovingPasses);
}
/**
* Gets all passes for the BeforeOptimization pass.
*
* @return array An array of passes
* @return CompilerPassInterface[]
*/
public function getBeforeOptimizationPasses()
{
return $this->beforeOptimizationPasses;
return $this->sortPasses($this->beforeOptimizationPasses);
}
/**
* Gets all passes for the BeforeRemoving pass.
*
* @return array An array of passes
* @return CompilerPassInterface[]
*/
public function getBeforeRemovingPasses()
{
return $this->beforeRemovingPasses;
return $this->sortPasses($this->beforeRemovingPasses);
}
/**
* Gets all passes for the Optimization pass.
*
* @return array An array of passes
* @return CompilerPassInterface[]
*/
public function getOptimizationPasses()
{
return $this->optimizationPasses;
return $this->sortPasses($this->optimizationPasses);
}
/**
* Gets all passes for the Removing pass.
*
* @return array An array of passes
* @return CompilerPassInterface[]
*/
public function getRemovingPasses()
{
return $this->removingPasses;
return $this->sortPasses($this->removingPasses);
}
/**
* Gets all passes for the Merge pass.
*
* @return array An array of passes
* @return CompilerPassInterface
*/
public function getMergePass()
{
@ -175,50 +188,69 @@ class PassConfig
/**
* Sets the AfterRemoving passes.
*
* @param array $passes An array of passes
* @param CompilerPassInterface[] $passes
*/
public function setAfterRemovingPasses(array $passes)
{
$this->afterRemovingPasses = $passes;
$this->afterRemovingPasses = array($passes);
}
/**
* Sets the BeforeOptimization passes.
*
* @param array $passes An array of passes
* @param CompilerPassInterface[] $passes
*/
public function setBeforeOptimizationPasses(array $passes)
{
$this->beforeOptimizationPasses = $passes;
$this->beforeOptimizationPasses = array($passes);
}
/**
* Sets the BeforeRemoving passes.
*
* @param array $passes An array of passes
* @param CompilerPassInterface[] $passes
*/
public function setBeforeRemovingPasses(array $passes)
{
$this->beforeRemovingPasses = $passes;
$this->beforeRemovingPasses = array($passes);
}
/**
* Sets the Optimization passes.
*
* @param array $passes An array of passes
* @param CompilerPassInterface[] $passes
*/
public function setOptimizationPasses(array $passes)
{
$this->optimizationPasses = $passes;
$this->optimizationPasses = array($passes);
}
/**
* Sets the Removing passes.
*
* @param array $passes An array of passes
* @param CompilerPassInterface[] $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.
*
* @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of compiler pass
* @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of compiler pass
* @param int $priority Used to sort the passes
*
* @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);

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\Component\Config\Resource\ResourceInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
@ -284,10 +285,14 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{
$builder = new ContainerBuilder();
$builder->setResourceTracking(false);
$builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses();
$builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'));
$defaultPasses = $builder->getCompiler()->getPassConfig()->getPasses();
$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()