Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\DependencyInjection\Compiler;
|
Chris@0
|
13
|
Chris@14
|
14 use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
|
Chris@17
|
15 use Symfony\Component\DependencyInjection\ContainerBuilder;
|
Chris@14
|
16 use Symfony\Component\DependencyInjection\ContainerInterface;
|
Chris@0
|
17 use Symfony\Component\DependencyInjection\Definition;
|
Chris@14
|
18 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
Chris@12
|
19 use Symfony\Component\DependencyInjection\ExpressionLanguage;
|
Chris@0
|
20 use Symfony\Component\DependencyInjection\Reference;
|
Chris@12
|
21 use Symfony\Component\ExpressionLanguage\Expression;
|
Chris@0
|
22
|
Chris@0
|
23 /**
|
Chris@0
|
24 * Run this pass before passes that need to know more about the relation of
|
Chris@0
|
25 * your services.
|
Chris@0
|
26 *
|
Chris@0
|
27 * This class will populate the ServiceReferenceGraph with information. You can
|
Chris@0
|
28 * retrieve the graph in other passes from the compiler.
|
Chris@0
|
29 *
|
Chris@0
|
30 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
Chris@0
|
31 */
|
Chris@14
|
32 class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface
|
Chris@0
|
33 {
|
Chris@0
|
34 private $graph;
|
Chris@0
|
35 private $currentDefinition;
|
Chris@0
|
36 private $onlyConstructorArguments;
|
Chris@17
|
37 private $hasProxyDumper;
|
Chris@14
|
38 private $lazy;
|
Chris@12
|
39 private $expressionLanguage;
|
Chris@17
|
40 private $byConstructor;
|
Chris@0
|
41
|
Chris@0
|
42 /**
|
Chris@0
|
43 * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
|
Chris@0
|
44 */
|
Chris@17
|
45 public function __construct($onlyConstructorArguments = false, $hasProxyDumper = true)
|
Chris@0
|
46 {
|
Chris@0
|
47 $this->onlyConstructorArguments = (bool) $onlyConstructorArguments;
|
Chris@17
|
48 $this->hasProxyDumper = (bool) $hasProxyDumper;
|
Chris@0
|
49 }
|
Chris@0
|
50
|
Chris@0
|
51 /**
|
Chris@0
|
52 * {@inheritdoc}
|
Chris@0
|
53 */
|
Chris@0
|
54 public function setRepeatedPass(RepeatedPass $repeatedPass)
|
Chris@0
|
55 {
|
Chris@14
|
56 // no-op for BC
|
Chris@0
|
57 }
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * Processes a ContainerBuilder object to populate the service reference graph.
|
Chris@0
|
61 */
|
Chris@0
|
62 public function process(ContainerBuilder $container)
|
Chris@0
|
63 {
|
Chris@0
|
64 $this->container = $container;
|
Chris@0
|
65 $this->graph = $container->getCompiler()->getServiceReferenceGraph();
|
Chris@0
|
66 $this->graph->clear();
|
Chris@14
|
67 $this->lazy = false;
|
Chris@17
|
68 $this->byConstructor = false;
|
Chris@0
|
69
|
Chris@14
|
70 foreach ($container->getAliases() as $id => $alias) {
|
Chris@14
|
71 $targetId = $this->getDefinitionId((string) $alias);
|
Chris@14
|
72 $this->graph->connect($id, $alias, $targetId, $this->getDefinition($targetId), null);
|
Chris@0
|
73 }
|
Chris@0
|
74
|
Chris@14
|
75 parent::process($container);
|
Chris@0
|
76 }
|
Chris@0
|
77
|
Chris@14
|
78 protected function processValue($value, $isRoot = false)
|
Chris@0
|
79 {
|
Chris@14
|
80 $lazy = $this->lazy;
|
Chris@0
|
81
|
Chris@14
|
82 if ($value instanceof ArgumentInterface) {
|
Chris@14
|
83 $this->lazy = true;
|
Chris@14
|
84 parent::processValue($value->getValues());
|
Chris@14
|
85 $this->lazy = $lazy;
|
Chris@14
|
86
|
Chris@14
|
87 return $value;
|
Chris@14
|
88 }
|
Chris@14
|
89 if ($value instanceof Expression) {
|
Chris@17
|
90 $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']);
|
Chris@14
|
91
|
Chris@14
|
92 return $value;
|
Chris@14
|
93 }
|
Chris@14
|
94 if ($value instanceof Reference) {
|
Chris@14
|
95 $targetId = $this->getDefinitionId((string) $value);
|
Chris@14
|
96 $targetDefinition = $this->getDefinition($targetId);
|
Chris@14
|
97
|
Chris@14
|
98 $this->graph->connect(
|
Chris@14
|
99 $this->currentId,
|
Chris@14
|
100 $this->currentDefinition,
|
Chris@14
|
101 $targetId,
|
Chris@14
|
102 $targetDefinition,
|
Chris@14
|
103 $value,
|
Chris@17
|
104 $this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()),
|
Chris@17
|
105 ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(),
|
Chris@17
|
106 $this->byConstructor
|
Chris@14
|
107 );
|
Chris@14
|
108
|
Chris@14
|
109 return $value;
|
Chris@14
|
110 }
|
Chris@14
|
111 if (!$value instanceof Definition) {
|
Chris@14
|
112 return parent::processValue($value, $isRoot);
|
Chris@14
|
113 }
|
Chris@14
|
114 if ($isRoot) {
|
Chris@14
|
115 if ($value->isSynthetic() || $value->isAbstract()) {
|
Chris@14
|
116 return $value;
|
Chris@0
|
117 }
|
Chris@14
|
118 $this->currentDefinition = $value;
|
Chris@17
|
119 } elseif ($this->currentDefinition === $value) {
|
Chris@17
|
120 return $value;
|
Chris@0
|
121 }
|
Chris@14
|
122 $this->lazy = false;
|
Chris@14
|
123
|
Chris@17
|
124 $byConstructor = $this->byConstructor;
|
Chris@17
|
125 $this->byConstructor = true;
|
Chris@14
|
126 $this->processValue($value->getFactory());
|
Chris@14
|
127 $this->processValue($value->getArguments());
|
Chris@17
|
128 $this->byConstructor = $byConstructor;
|
Chris@14
|
129
|
Chris@14
|
130 if (!$this->onlyConstructorArguments) {
|
Chris@14
|
131 $this->processValue($value->getProperties());
|
Chris@14
|
132 $this->processValue($value->getMethodCalls());
|
Chris@14
|
133 $this->processValue($value->getConfigurator());
|
Chris@14
|
134 }
|
Chris@14
|
135 $this->lazy = $lazy;
|
Chris@14
|
136
|
Chris@14
|
137 return $value;
|
Chris@0
|
138 }
|
Chris@0
|
139
|
Chris@0
|
140 /**
|
Chris@0
|
141 * Returns a service definition given the full name or an alias.
|
Chris@0
|
142 *
|
Chris@0
|
143 * @param string $id A full id or alias for a service definition
|
Chris@0
|
144 *
|
Chris@0
|
145 * @return Definition|null The definition related to the supplied id
|
Chris@0
|
146 */
|
Chris@0
|
147 private function getDefinition($id)
|
Chris@0
|
148 {
|
Chris@0
|
149 return null === $id ? null : $this->container->getDefinition($id);
|
Chris@0
|
150 }
|
Chris@0
|
151
|
Chris@0
|
152 private function getDefinitionId($id)
|
Chris@0
|
153 {
|
Chris@0
|
154 while ($this->container->hasAlias($id)) {
|
Chris@0
|
155 $id = (string) $this->container->getAlias($id);
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@0
|
158 if (!$this->container->hasDefinition($id)) {
|
Chris@0
|
159 return;
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@14
|
162 return $this->container->normalizeId($id);
|
Chris@0
|
163 }
|
Chris@12
|
164
|
Chris@12
|
165 private function getExpressionLanguage()
|
Chris@12
|
166 {
|
Chris@12
|
167 if (null === $this->expressionLanguage) {
|
Chris@14
|
168 if (!class_exists(ExpressionLanguage::class)) {
|
Chris@14
|
169 throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
Chris@14
|
170 }
|
Chris@14
|
171
|
Chris@12
|
172 $providers = $this->container->getExpressionLanguageProviders();
|
Chris@12
|
173 $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
|
Chris@12
|
174 if ('""' === substr_replace($arg, '', 1, -1)) {
|
Chris@12
|
175 $id = stripcslashes(substr($arg, 1, -1));
|
Chris@14
|
176 $id = $this->getDefinitionId($id);
|
Chris@12
|
177
|
Chris@12
|
178 $this->graph->connect(
|
Chris@12
|
179 $this->currentId,
|
Chris@12
|
180 $this->currentDefinition,
|
Chris@14
|
181 $id,
|
Chris@12
|
182 $this->getDefinition($id)
|
Chris@12
|
183 );
|
Chris@12
|
184 }
|
Chris@12
|
185
|
Chris@12
|
186 return sprintf('$this->get(%s)', $arg);
|
Chris@12
|
187 });
|
Chris@12
|
188 }
|
Chris@12
|
189
|
Chris@12
|
190 return $this->expressionLanguage;
|
Chris@12
|
191 }
|
Chris@0
|
192 }
|