Skip to content

Commit

Permalink
feature #21327 [DI] Factorize compiler passes around new AbstractRecu…
Browse files Browse the repository at this point in the history
…rsivePass (nicolas-grekas)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[DI] Factorize compiler passes around new AbstractRecursivePass

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

This PR introduces an AbstractRecursivePass that is able to visit the definition tree recursively everywhere a Definition or a Reference can be nested.

All existing compiler passes that need recursivity are updated to leverage this new class.
This remove a bunch of boilerplate that was previously repeated all over.
It also fixes compiler passes that eg missed looking at configurators for no reason (this "fix" is a new feature really).
It then applies recursivity to AutowirePass, that does not handle it today, but should.

I'm happy that the net result is a loss of 153 lines :)

Commits
-------

6acb80f [DI] Factorize compiler passes around new AbstractRecursivePass
  • Loading branch information
fabpot committed Jan 23, 2017
2 parents 3697e1e + 6acb80f commit 3f8aa7b
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 483 deletions.
@@ -0,0 +1,76 @@
<?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\Compiler;

use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
abstract class AbstractRecursivePass implements CompilerPassInterface
{
protected $container;
protected $currentId;

/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;

try {
$this->processValue($container->getDefinitions(), true);
} finally {
$this->container = null;
}
}

/**
* Processes a value found in a definition tree.
*
* @param mixed $value
* @param bool $isRoot
*
* @return mixed The processed value
*/
protected function processValue($value, $isRoot = false)
{
if (is_array($value)) {
foreach ($value as $k => $v) {
if ($isRoot) {
$this->currentId = $k;
}
if ($v !== $processedValue = $this->processValue($v, $isRoot)) {
$value[$k] = $processedValue;
}
}
} elseif ($value instanceof ArgumentInterface) {
$value->setValues($this->processValue($value->getValues()));
} elseif ($value instanceof Definition) {
$value->setArguments($this->processValue($value->getArguments()));
$value->setProperties($this->processValue($value->getProperties()));
$value->setMethodCalls($this->processValue($value->getMethodCalls()));

if ($v = $value->getFactory()) {
$value->setFactory($this->processValue($v));
}
if ($v = $value->getConfigurator()) {
$value->setConfigurator($this->processValue($v));
}
}

return $value;
}
}
Expand Up @@ -25,14 +25,13 @@
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AnalyzeServiceReferencesPass implements RepeatablePassInterface
class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface
{
private $graph;
private $container;
private $currentId;
private $currentDefinition;
private $repeatedPass;
private $onlyConstructorArguments;
private $lazy;

/**
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
Expand Down Expand Up @@ -60,68 +59,60 @@ public function process(ContainerBuilder $container)
$this->container = $container;
$this->graph = $container->getCompiler()->getServiceReferenceGraph();
$this->graph->clear();

foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isSynthetic() || $definition->isAbstract()) {
continue;
}

$this->currentId = $id;
$this->currentDefinition = $definition;

$this->processArguments($definition->getArguments());
if (is_array($definition->getFactory())) {
$this->processArguments($definition->getFactory());
}

if (!$this->onlyConstructorArguments) {
$this->processArguments($definition->getMethodCalls());
$this->processArguments($definition->getProperties());
if ($definition->getConfigurator()) {
$this->processArguments(array($definition->getConfigurator()));
}
}
}
$this->lazy = false;

foreach ($container->getAliases() as $id => $alias) {
$this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null);
}

parent::process($container);
}

/**
* Processes service definitions for arguments to find relationships for the service graph.
*
* @param array $arguments An array of Reference or Definition objects relating to service definitions
* @param bool $lazy Whether the references nested in the arguments should be considered lazy or not
*/
private function processArguments(array $arguments, $lazy = false)
protected function processValue($value, $isRoot = false)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
$this->processArguments($argument, $lazy);
} elseif ($argument instanceof ArgumentInterface) {
$this->processArguments($argument->getValues(), true);
} elseif ($argument instanceof Reference) {
$targetDefinition = $this->getDefinition((string) $argument);

$this->graph->connect(
$this->currentId,
$this->currentDefinition,
$this->getDefinitionId((string) $argument),
$targetDefinition,
$argument,
$lazy || ($targetDefinition && $targetDefinition->isLazy())
);
} elseif ($argument instanceof Definition) {
$this->processArguments($argument->getArguments());
$this->processArguments($argument->getMethodCalls());
$this->processArguments($argument->getProperties());

if (is_array($argument->getFactory())) {
$this->processArguments($argument->getFactory());
}
$lazy = $this->lazy;

if ($value instanceof ArgumentInterface) {
$this->lazy = true;
parent::processValue($value);
$this->lazy = $lazy;

return $value;
}
if ($value instanceof Reference) {
$targetDefinition = $this->getDefinition((string) $value);

$this->graph->connect(
$this->currentId,
$this->currentDefinition,
$this->getDefinitionId((string) $value),
$targetDefinition,
$value,
$this->lazy || ($targetDefinition && $targetDefinition->isLazy())
);

return $value;
}
if (!$value instanceof Definition) {
return parent::processValue($value, $isRoot);
}
if ($isRoot) {
if ($value->isSynthetic() || $value->isAbstract()) {
return $value;
}
$this->currentDefinition = $value;
}
$this->lazy = false;

if ($this->onlyConstructorArguments) {
$this->processValue($value->getFactory());
$this->processValue($value->getArguments());
} else {
parent::processValue($value, $isRoot);
}
$this->lazy = $lazy;

return $value;
}

/**
Expand Down

0 comments on commit 3f8aa7b

Please sign in to comment.