[DependencyInjection] performance improvement, better analysis tools
This commit is contained in:
parent
e85546ef7d
commit
d1a2a65d19
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Run this pass before passes that need to know more about the relation of
|
||||
* your services.
|
||||
*
|
||||
* This class will populate the ServiceReferenceGraph with information. You can
|
||||
* retrieve the graph in other passes from the compiler.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class AnalyzeServiceReferencesPass implements RepeatablePassInterface, CompilerAwareInterface
|
||||
{
|
||||
protected $graph;
|
||||
protected $container;
|
||||
protected $currentId;
|
||||
protected $currentDefinition;
|
||||
protected $repeatedPass;
|
||||
|
||||
public function setRepeatedPass(RepeatedPass $repeatedPass) {
|
||||
$this->repeatedPass = $repeatedPass;
|
||||
}
|
||||
|
||||
public function setCompiler(Compiler $compiler)
|
||||
{
|
||||
$this->graph = $compiler->getServiceReferenceGraph();
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
if (null === $this->graph) {
|
||||
$this->graph = $this->repeatedPass->getCompiler()->getServiceReferenceGraph();
|
||||
}
|
||||
$this->graph->clear();
|
||||
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
$this->currentId = $id;
|
||||
$this->currentDefinition = $definition;
|
||||
$this->processArguments($definition->getArguments());
|
||||
$this->processArguments($definition->getMethodCalls());
|
||||
}
|
||||
|
||||
foreach ($container->getAliases() as $id => $alias) {
|
||||
$this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processArguments(array $arguments)
|
||||
{
|
||||
foreach ($arguments as $k => $argument) {
|
||||
if (is_array($argument)) {
|
||||
$this->processArguments($argument);
|
||||
} else if ($argument instanceof Reference) {
|
||||
$this->graph->connect(
|
||||
$this->currentId,
|
||||
$this->currentDefinition,
|
||||
(string) $argument,
|
||||
$this->getDefinition((string) $argument),
|
||||
$argument
|
||||
);
|
||||
} else if ($argument instanceof Definition) {
|
||||
$this->processArguments($argument->getArguments());
|
||||
$this->processArguments($argument->getMethodCalls());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getDefinition($id)
|
||||
{
|
||||
while ($this->container->hasAlias($id)) {
|
||||
$id = (string) $this->container->getAlias($id);
|
||||
}
|
||||
|
||||
if (!$this->container->hasDefinition($id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->container->getDefinition($id);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* This class is used to remove circular dependencies between individual passes.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class Compiler
|
||||
{
|
||||
protected $passConfig;
|
||||
protected $currentPass;
|
||||
protected $currentStartTime;
|
||||
protected $log;
|
||||
protected $serviceReferenceGraph;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->passConfig = new PassConfig();
|
||||
$this->serviceReferenceGraph = new ServiceReferenceGraph();
|
||||
$this->log = array();
|
||||
}
|
||||
|
||||
public function getPassConfig()
|
||||
{
|
||||
return $this->passConfig;
|
||||
}
|
||||
|
||||
public function getServiceReferenceGraph()
|
||||
{
|
||||
return $this->serviceReferenceGraph;
|
||||
}
|
||||
|
||||
public function addPass(CompilerPassInterface $pass)
|
||||
{
|
||||
$this->passConfig->addPass($pass);
|
||||
}
|
||||
|
||||
public function addLogMessage($string)
|
||||
{
|
||||
$this->log[] = $string;
|
||||
}
|
||||
|
||||
public function getLog()
|
||||
{
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
public function compile(ContainerBuilder $container)
|
||||
{
|
||||
foreach ($this->passConfig->getPasses() as $pass) {
|
||||
$this->startPass($pass);
|
||||
$pass->process($container);
|
||||
$this->endPass($pass);
|
||||
}
|
||||
}
|
||||
|
||||
protected function startPass(CompilerPassInterface $pass)
|
||||
{
|
||||
if ($pass instanceof CompilerAwareInterface) {
|
||||
$pass->setCompiler($this);
|
||||
}
|
||||
|
||||
$this->currentPass = $pass;
|
||||
$this->currentStartTime = microtime(true);
|
||||
}
|
||||
|
||||
protected function endPass(CompilerPassInterface $pass)
|
||||
{
|
||||
$this->currentPass = null;
|
||||
$this->addLogMessage(sprintf('%s finished in %.3fs', get_class($pass), microtime(true) - $this->currentStartTime));
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
/**
|
||||
* This interface can be implemented by passes that need to access the
|
||||
* compiler.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface CompilerAwareInterface
|
||||
{
|
||||
function setCompiler(Compiler $compiler);
|
||||
}
|
@ -23,8 +23,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
*/
|
||||
class InlineServiceDefinitionsPass implements RepeatablePassInterface
|
||||
{
|
||||
protected $aliasMap;
|
||||
protected $repeatedPass;
|
||||
protected $graph;
|
||||
|
||||
public function setRepeatedPass(RepeatedPass $repeatedPass)
|
||||
{
|
||||
@ -33,14 +33,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$this->aliasMap = array();
|
||||
foreach ($container->getAliases() as $id => $alias) {
|
||||
if (!$alias->isPublic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->aliasMap[$id] = (string) $alias;
|
||||
}
|
||||
$this->graph = $this->repeatedPass->getCompiler()->getServiceReferenceGraph();
|
||||
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
$definition->setArguments(
|
||||
@ -82,43 +75,15 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
$references = count(array_keys($this->aliasMap, $id, true));
|
||||
foreach ($container->getDefinitions() as $cDefinition)
|
||||
{
|
||||
if ($references > 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->isReferencedByArgument($id, $cDefinition->getArguments())) {
|
||||
$references += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($cDefinition->getMethodCalls() as $call) {
|
||||
if ($this->isReferencedByArgument($id, $call[1])) {
|
||||
$references += 1;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if (!$this->graph->hasNode($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $references <= 1;
|
||||
}
|
||||
|
||||
protected function isReferencedByArgument($id, $argument)
|
||||
{
|
||||
if (is_array($argument)) {
|
||||
foreach ($argument as $arg) {
|
||||
if ($this->isReferencedByArgument($id, $arg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if ($argument instanceof Reference) {
|
||||
if ($id === (string) $argument) {
|
||||
return true;
|
||||
}
|
||||
$ids = array();
|
||||
foreach ($this->graph->getNode($id)->getInEdges() as $edge) {
|
||||
$ids[] = $edge->getSourceNode()->getId();
|
||||
}
|
||||
|
||||
return false;
|
||||
return count(array_unique($ids)) <= 1;
|
||||
}
|
||||
}
|
@ -14,14 +14,22 @@ namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
/**
|
||||
* Compiler Pass Configuration
|
||||
*
|
||||
* This class has a default configuration embedded.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class PassConfig
|
||||
{
|
||||
const TYPE_AFTER_REMOVING = 'afterRemoving';
|
||||
const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization';
|
||||
const TYPE_BEFORE_REMOVING = 'beforeRemoving';
|
||||
const TYPE_OPTIMIZE = 'optimization';
|
||||
const TYPE_REMOVE = 'removing';
|
||||
|
||||
protected $mergePass;
|
||||
protected $afterRemovingPasses;
|
||||
protected $beforeOptimizationPasses;
|
||||
protected $beforeRemovingPasses;
|
||||
protected $optimizationPasses;
|
||||
protected $removingPasses;
|
||||
|
||||
@ -29,6 +37,10 @@ class PassConfig
|
||||
{
|
||||
$this->mergePass = new MergeExtensionConfigurationPass();
|
||||
|
||||
$this->afterRemovingPasses = array();
|
||||
$this->beforeOptimizationPasses = array();
|
||||
$this->beforeRemovingPasses = array();
|
||||
|
||||
$this->optimizationPasses = array(
|
||||
new ResolveParameterPlaceHoldersPass(),
|
||||
new ResolveReferencesToAliasesPass(),
|
||||
@ -40,7 +52,9 @@ class PassConfig
|
||||
new RemovePrivateAliasesPass(),
|
||||
new ReplaceAliasByActualDefinitionPass(),
|
||||
new RepeatedPass(array(
|
||||
new AnalyzeServiceReferencesPass(),
|
||||
new InlineServiceDefinitionsPass(),
|
||||
new AnalyzeServiceReferencesPass(),
|
||||
new RemoveUnusedDefinitionsPass(),
|
||||
)),
|
||||
);
|
||||
@ -50,12 +64,15 @@ class PassConfig
|
||||
{
|
||||
return array_merge(
|
||||
array($this->mergePass),
|
||||
$this->beforeOptimizationPasses,
|
||||
$this->optimizationPasses,
|
||||
$this->removingPasses
|
||||
$this->beforeRemovingPasses,
|
||||
$this->removingPasses,
|
||||
$this->afterRemovingPasses
|
||||
);
|
||||
}
|
||||
|
||||
public function addPass(CompilerPassInterface $pass, $type = self::TYPE_OPTIMIZE)
|
||||
public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION)
|
||||
{
|
||||
$property = $type.'Passes';
|
||||
if (!isset($this->$property)) {
|
||||
@ -66,6 +83,16 @@ class PassConfig
|
||||
$passes[] = $pass;
|
||||
}
|
||||
|
||||
public function getBeforeOptimizationPasses()
|
||||
{
|
||||
return $this->beforeOptimizationPasses;
|
||||
}
|
||||
|
||||
public function getBeforeRemovingPasses()
|
||||
{
|
||||
return $this->beforeRemovingPasses;
|
||||
}
|
||||
|
||||
public function getOptimizationPasses()
|
||||
{
|
||||
return $this->optimizationPasses;
|
||||
@ -86,6 +113,16 @@ class PassConfig
|
||||
$this->mergePass = $pass;
|
||||
}
|
||||
|
||||
public function setBeforeOptimizationPasses(array $passes)
|
||||
{
|
||||
$this->beforeOptimizationPasses = $passes;
|
||||
}
|
||||
|
||||
public function setBeforeRemovingPasses(array $passes)
|
||||
{
|
||||
$this->beforeRemovingPasses = $passes;
|
||||
}
|
||||
|
||||
public function setOptimizationPasses(array $passes)
|
||||
{
|
||||
$this->optimizationPasses = $passes;
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@ -23,6 +25,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
|
||||
{
|
||||
protected $repeatedPass;
|
||||
protected $graph;
|
||||
|
||||
public function setRepeatedPass(RepeatedPass $repeatedPass)
|
||||
{
|
||||
@ -31,18 +34,31 @@ class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$this->graph = $this->repeatedPass->getCompiler()->getServiceReferenceGraph();
|
||||
|
||||
$hasChanged = false;
|
||||
$aliases = $container->getAliases();
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
if ($definition->isPublic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$referencingAliases = array_keys($aliases, $id, true);
|
||||
$isReferenced = $this->isReferenced($container, $id);
|
||||
if ($this->graph->hasNode($id)) {
|
||||
$edges = $this->graph->getNode($id)->getInEdges();
|
||||
$referencingAliases = array();
|
||||
foreach ($edges as $edge) {
|
||||
$node = $edge->getSourceNode();
|
||||
if ($node->isAlias()) {
|
||||
$referencingAlias[] = $node->getValue();
|
||||
}
|
||||
}
|
||||
$isReferenced = (count($edges) - count($referencingAliases)) > 0;
|
||||
} else {
|
||||
$referencingAliases = array();
|
||||
$isReferenced = false;
|
||||
}
|
||||
|
||||
if (1 === count($referencingAliases) && false === $isReferenced) {
|
||||
$container->setDefinition(reset($referencingAliases), $definition);
|
||||
$container->setDefinition((string) reset($referencingAliases), $definition);
|
||||
$definition->setPublic(true);
|
||||
$container->remove($id);
|
||||
} else if (0 === count($referencingAliases) && false === $isReferenced) {
|
||||
@ -55,44 +71,4 @@ class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
|
||||
$this->repeatedPass->setRepeat();
|
||||
}
|
||||
}
|
||||
|
||||
protected function isReferenced(ContainerBuilder $container, $id)
|
||||
{
|
||||
foreach ($container->getDefinitions() as $definition) {
|
||||
if ($this->isReferencedByArgument($id, $definition->getArguments())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isReferencedByArgument($id, $definition->getMethodCalls())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isReferencedByArgument($id, $argument)
|
||||
{
|
||||
if (is_array($argument)) {
|
||||
foreach ($argument as $arg) {
|
||||
if ($this->isReferencedByArgument($id, $arg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if ($argument instanceof Reference) {
|
||||
if ($id === (string) $argument) {
|
||||
return true;
|
||||
}
|
||||
} else if ($argument instanceof Definition) {
|
||||
if ($this->isReferencedByArgument($id, $argument->getArguments())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isReferencedByArgument($id, $argument->getMethodCalls())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -9,9 +9,10 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class RepeatedPass implements CompilerPassInterface
|
||||
class RepeatedPass implements CompilerPassInterface, CompilerAwareInterface
|
||||
{
|
||||
protected $repeat;
|
||||
protected $compiler;
|
||||
protected $passes;
|
||||
|
||||
public function __construct(array $passes)
|
||||
@ -27,11 +28,25 @@ class RepeatedPass implements CompilerPassInterface
|
||||
$this->passes = $passes;
|
||||
}
|
||||
|
||||
public function setCompiler(Compiler $compiler)
|
||||
{
|
||||
$this->compiler = $compiler;
|
||||
}
|
||||
|
||||
public function getCompiler()
|
||||
{
|
||||
return $this->compiler;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$this->repeat = false;
|
||||
foreach ($this->passes as $pass) {
|
||||
$time = microtime(true);
|
||||
$pass->process($container);
|
||||
$this->compiler->addLogMessage(sprintf(
|
||||
'%s finished in %.3fs', get_class($pass), microtime(true) - $time
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->repeat) {
|
||||
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
/**
|
||||
* This is a directed graph of your services.
|
||||
*
|
||||
* This information can be used by your compiler passes instead of collecting
|
||||
* it themselves which improves performance quite a lot.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class ServiceReferenceGraph
|
||||
{
|
||||
protected $nodes;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nodes = array();
|
||||
}
|
||||
|
||||
public function hasNode($id)
|
||||
{
|
||||
return isset($this->nodes[$id]);
|
||||
}
|
||||
|
||||
public function getNode($id)
|
||||
{
|
||||
if (!isset($this->nodes[$id])) {
|
||||
throw new \InvalidArgumentException(sprintf('There is no node with id "%s".', $id));
|
||||
}
|
||||
|
||||
return $this->nodes[$id];
|
||||
}
|
||||
|
||||
public function getNodes()
|
||||
{
|
||||
return $this->nodes;
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
$this->nodes = array();
|
||||
}
|
||||
|
||||
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null)
|
||||
{
|
||||
$sourceNode = $this->createNode($sourceId, $sourceValue);
|
||||
$destNode = $this->createNode($destId, $destValue);
|
||||
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference);
|
||||
|
||||
$sourceNode->addOutEdge($edge);
|
||||
$destNode->addInEdge($edge);
|
||||
}
|
||||
|
||||
protected function createNode($id, $value)
|
||||
{
|
||||
if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) {
|
||||
return $this->nodes[$id];
|
||||
}
|
||||
|
||||
return $this->nodes[$id] = new ServiceReferenceGraphNode($id, $value);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
/**
|
||||
* Represents an edge in your service graph.
|
||||
*
|
||||
* Value is typically a reference.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class ServiceReferenceGraphEdge
|
||||
{
|
||||
protected $sourceNode;
|
||||
protected $destNode;
|
||||
protected $value;
|
||||
|
||||
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null)
|
||||
{
|
||||
$this->sourceNode = $sourceNode;
|
||||
$this->destNode = $destNode;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getSourceNode()
|
||||
{
|
||||
return $this->sourceNode;
|
||||
}
|
||||
|
||||
public function getDestNode()
|
||||
{
|
||||
return $this->destNode;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
|
||||
/**
|
||||
* Represents a node in your service graph.
|
||||
*
|
||||
* Value is typically a definition, or an alias.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class ServiceReferenceGraphNode
|
||||
{
|
||||
protected $id;
|
||||
protected $inEdges;
|
||||
protected $outEdges;
|
||||
protected $value;
|
||||
|
||||
public function __construct($id, $value)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->value = $value;
|
||||
$this->inEdges = array();
|
||||
$this->outEdges = array();
|
||||
}
|
||||
|
||||
public function addInEdge(ServiceReferenceGraphEdge $edge)
|
||||
{
|
||||
$this->inEdges[] = $edge;
|
||||
}
|
||||
|
||||
public function addOutEdge(ServiceReferenceGraphEdge $edge)
|
||||
{
|
||||
$this->outEdges[] = $edge;
|
||||
}
|
||||
|
||||
public function isAlias()
|
||||
{
|
||||
return $this->value instanceof Alias;
|
||||
}
|
||||
|
||||
public function isDefinition()
|
||||
{
|
||||
return $this->value instanceof Definition;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getInEdges()
|
||||
{
|
||||
return $this->inEdges;
|
||||
}
|
||||
|
||||
public function getOutEdges()
|
||||
{
|
||||
return $this->outEdges;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
@ -2,12 +2,7 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveInterfaceInjectorsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\Compiler;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\InterfaceInjector;
|
||||
@ -39,7 +34,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
protected $resources = array();
|
||||
protected $extensionConfigs = array();
|
||||
protected $injectors = array();
|
||||
protected $compilerPassConfig;
|
||||
protected $compiler;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -49,7 +44,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
{
|
||||
parent::__construct($parameterBag);
|
||||
|
||||
$this->compilerPassConfig = new PassConfig();
|
||||
$this->compiler = new Compiler();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,7 +149,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
*/
|
||||
public function addCompilerPass(CompilerPassInterface $pass)
|
||||
{
|
||||
$this->compilerPassConfig->addPass($pass);
|
||||
$this->compiler->addPass($pass);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,7 +159,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
*/
|
||||
public function getCompilerPassConfig()
|
||||
{
|
||||
return $this->compilerPassConfig;
|
||||
return $this->compiler->getPassConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the compiler instance
|
||||
*
|
||||
* @return Compiler
|
||||
*/
|
||||
public function getCompiler()
|
||||
{
|
||||
return $this->compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -335,9 +340,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
*/
|
||||
public function freeze()
|
||||
{
|
||||
foreach ($this->compilerPassConfig->getPasses() as $pass) {
|
||||
$pass->process($this);
|
||||
}
|
||||
$this->compiler->compile($this);
|
||||
|
||||
parent::freeze();
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\Compiler;
|
||||
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class AnalyzeServiceReferencesPassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$a = $container
|
||||
->register('a')
|
||||
->addArgument($ref1 = new Reference('b'))
|
||||
;
|
||||
|
||||
$b = $container
|
||||
->register('b')
|
||||
->addMethodCall('setA', array($ref2 = new Reference('a')))
|
||||
;
|
||||
|
||||
$c = $container
|
||||
->register('c')
|
||||
->addArgument($ref3 = new Reference('a'))
|
||||
->addArgument($ref4 = new Reference('b'))
|
||||
;
|
||||
|
||||
$graph = $this->process($container);
|
||||
|
||||
$this->assertEquals(2, count($edges = $graph->getNode('b')->getInEdges()));
|
||||
$this->assertSame($ref1, $edges[0]->getValue());
|
||||
$this->assertSame($ref4, $edges[1]->getValue());
|
||||
}
|
||||
|
||||
public function testProcessDetectsReferencesFromInlinedDefinitions()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container
|
||||
->register('a')
|
||||
;
|
||||
|
||||
$container
|
||||
->register('b')
|
||||
->addArgument(new Definition(null, array($ref = new Reference('a'))))
|
||||
;
|
||||
|
||||
$graph = $this->process($container);
|
||||
|
||||
$this->assertEquals(1, count($refs = $graph->getNode('a')->getInEdges()));
|
||||
$this->assertSame($ref, $refs[0]->getValue());
|
||||
}
|
||||
|
||||
public function testProcessDoesNotSaveDuplicateReferences()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container
|
||||
->register('a')
|
||||
;
|
||||
$container
|
||||
->register('b')
|
||||
->addArgument(new Definition(null, array($ref1 = new Reference('a'))))
|
||||
->addArgument(new Definition(null, array($ref2 = new Reference('a'))))
|
||||
;
|
||||
|
||||
$graph = $this->process($container);
|
||||
|
||||
$this->assertEquals(2, count($graph->getNode('a')->getInEdges()));
|
||||
}
|
||||
|
||||
protected function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new RepeatedPass(array(new AnalyzeServiceReferencesPass()));
|
||||
$pass->setCompiler($compiler = new Compiler());
|
||||
$pass->process($container);
|
||||
|
||||
return $compiler->getServiceReferenceGraph();
|
||||
}
|
||||
}
|
@ -2,6 +2,12 @@
|
||||
|
||||
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@ -77,7 +83,8 @@ class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new InlineServiceDefinitionsPass();
|
||||
$pass->process($container);
|
||||
$repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass()));
|
||||
$repeatedPass->setCompiler(new Compiler());
|
||||
$repeatedPass->process($container);
|
||||
}
|
||||
}
|
@ -2,8 +2,9 @@
|
||||
|
||||
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\Compiler;
|
||||
use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
@ -73,8 +74,8 @@ class RemoveUnusedDefinitionsPassTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new RemoveUnusedDefinitionsPass();
|
||||
$repeatedPass = new RepeatedPass(array($pass));
|
||||
$pass->process($container);
|
||||
$repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass()));
|
||||
$repeatedPass->setCompiler(new Compiler());
|
||||
$repeatedPass->process($container);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user