-
-
Notifications
You must be signed in to change notification settings - Fork 91
/
ReplaceAliasByActualDefinitionPass.php
142 lines (130 loc) · 5.53 KB
/
ReplaceAliasByActualDefinitionPass.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?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\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Replaces aliases with actual service definitions, effectively removing these
* aliases.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
{
private $compiler;
private $formatter;
/**
* Process the Container to replace aliases with service definitions.
*
* @throws InvalidArgumentException if the service definition does not exist
*/
public function process(ContainerBuilder $container)
{
// Setup
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
// First collect all alias targets that need to be replaced
$seenAliasTargets = array();
$replacements = array();
foreach ($container->getAliases() as $definitionId => $target) {
$targetId = (string) $target;
// Special case: leave this target alone
if ('service_container' === $targetId) {
continue;
}
// Check if target needs to be replaces
if (isset($replacements[$targetId])) {
$container->setAlias($definitionId, $replacements[$targetId]);
}
// No need to process the same target twice
if (isset($seenAliasTargets[$targetId])) {
continue;
}
// Process new target
$seenAliasTargets[$targetId] = true;
try {
$definition = $container->getDefinition($targetId);
} catch (InvalidArgumentException $e) {
throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e);
}
if ($definition->isPublic()) {
continue;
}
// Remove private definition and schedule for replacement
$definition->setPublic(true);
$container->setDefinition($definitionId, $definition);
$container->removeDefinition($targetId);
$replacements[$targetId] = $definitionId;
}
// Now replace target instances in all definitions
foreach ($container->getDefinitions() as $definitionId => $definition) {
$definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
$definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
$definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
$definition->setFactoryService($this->updateFactoryReferenceId($replacements, $definition->getFactoryService(false)), false);
$definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
}
}
/**
* Recursively updates references in an array.
*
* @param array $replacements Table of aliases to replace
* @param string $definitionId Identifier of this definition
* @param array $arguments Where to replace the aliases
*
* @return array
*/
private function updateArgumentReferences(array $replacements, $definitionId, array $arguments)
{
foreach ($arguments as $k => $argument) {
// Handle recursion step
if (is_array($argument)) {
$arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument);
continue;
}
// Skip arguments that don't need replacement
if (!$argument instanceof Reference) {
continue;
}
$referenceId = (string) $argument;
if (!isset($replacements[$referenceId])) {
continue;
}
// Perform the replacement
$newId = $replacements[$referenceId];
$arguments[$k] = new Reference($newId, $argument->getInvalidBehavior());
$this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId));
}
return $arguments;
}
/**
* Returns the updated reference for the factory service.
*
* @param array $replacements Table of aliases to replace
* @param string|null $referenceId Factory service reference identifier
*
* @return string|null
*/
private function updateFactoryReferenceId(array $replacements, $referenceId)
{
if (null === $referenceId) {
return;
}
return isset($replacements[$referenceId]) ? $replacements[$referenceId] : $referenceId;
}
private function updateFactoryReference(array $replacements, $factory)
{
if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) {
$factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior());
}
return $factory;
}
}