Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/dependency-injection/Dumper/PhpDumper.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | 7a779792577d |
children | c2387f117808 |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
9 * file that was distributed with this source code. | 9 * file that was distributed with this source code. |
10 */ | 10 */ |
11 | 11 |
12 namespace Symfony\Component\DependencyInjection\Dumper; | 12 namespace Symfony\Component\DependencyInjection\Dumper; |
13 | 13 |
14 use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; | |
15 use Symfony\Component\DependencyInjection\Argument\IteratorArgument; | |
16 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; | |
14 use Symfony\Component\DependencyInjection\Variable; | 17 use Symfony\Component\DependencyInjection\Variable; |
15 use Symfony\Component\DependencyInjection\Definition; | 18 use Symfony\Component\DependencyInjection\Definition; |
19 use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; | |
16 use Symfony\Component\DependencyInjection\ContainerBuilder; | 20 use Symfony\Component\DependencyInjection\ContainerBuilder; |
17 use Symfony\Component\DependencyInjection\Container; | 21 use Symfony\Component\DependencyInjection\Container; |
18 use Symfony\Component\DependencyInjection\ContainerInterface; | 22 use Symfony\Component\DependencyInjection\ContainerInterface; |
19 use Symfony\Component\DependencyInjection\Reference; | 23 use Symfony\Component\DependencyInjection\Reference; |
24 use Symfony\Component\DependencyInjection\TypedReference; | |
20 use Symfony\Component\DependencyInjection\Parameter; | 25 use Symfony\Component\DependencyInjection\Parameter; |
21 use Symfony\Component\DependencyInjection\Exception\EnvParameterException; | 26 use Symfony\Component\DependencyInjection\Exception\EnvParameterException; |
22 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; | 27 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; |
23 use Symfony\Component\DependencyInjection\Exception\RuntimeException; | 28 use Symfony\Component\DependencyInjection\Exception\RuntimeException; |
24 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; | 29 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; |
36 */ | 41 */ |
37 class PhpDumper extends Dumper | 42 class PhpDumper extends Dumper |
38 { | 43 { |
39 /** | 44 /** |
40 * Characters that might appear in the generated variable name as first character. | 45 * Characters that might appear in the generated variable name as first character. |
41 * | |
42 * @var string | |
43 */ | 46 */ |
44 const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; | 47 const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; |
45 | 48 |
46 /** | 49 /** |
47 * Characters that might appear in the generated variable name as any but the first character. | 50 * Characters that might appear in the generated variable name as any but the first character. |
48 * | |
49 * @var string | |
50 */ | 51 */ |
51 const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; | 52 const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; |
52 | 53 |
53 private $inlinedDefinitions; | |
54 private $definitionVariables; | 54 private $definitionVariables; |
55 private $referenceVariables; | 55 private $referenceVariables; |
56 private $variableCount; | 56 private $variableCount; |
57 private $reservedVariables = array('instance', 'class'); | 57 private $reservedVariables = array('instance', 'class'); |
58 private $expressionLanguage; | 58 private $expressionLanguage; |
59 private $targetDirRegex; | 59 private $targetDirRegex; |
60 private $targetDirMaxMatches; | 60 private $targetDirMaxMatches; |
61 private $docStar; | 61 private $docStar; |
62 private $serviceIdToMethodNameMap; | 62 private $serviceIdToMethodNameMap; |
63 private $usedMethodNames; | 63 private $usedMethodNames; |
64 | 64 private $namespace; |
65 /** | 65 private $asFiles; |
66 * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface | 66 private $hotPathTag; |
67 private $inlineRequires; | |
68 private $inlinedRequires = array(); | |
69 private $circularReferences = array(); | |
70 | |
71 /** | |
72 * @var ProxyDumper | |
67 */ | 73 */ |
68 private $proxyDumper; | 74 private $proxyDumper; |
69 | 75 |
70 /** | 76 /** |
71 * {@inheritdoc} | 77 * {@inheritdoc} |
72 */ | 78 */ |
73 public function __construct(ContainerBuilder $container) | 79 public function __construct(ContainerBuilder $container) |
74 { | 80 { |
81 if (!$container->isCompiled()) { | |
82 @trigger_error('Dumping an uncompiled ContainerBuilder is deprecated since Symfony 3.3 and will not be supported anymore in 4.0. Compile the container beforehand.', E_USER_DEPRECATED); | |
83 } | |
84 | |
75 parent::__construct($container); | 85 parent::__construct($container); |
76 | |
77 $this->inlinedDefinitions = new \SplObjectStorage(); | |
78 } | 86 } |
79 | 87 |
80 /** | 88 /** |
81 * Sets the dumper to be used when dumping proxies in the generated container. | 89 * Sets the dumper to be used when dumping proxies in the generated container. |
82 * | |
83 * @param ProxyDumper $proxyDumper | |
84 */ | 90 */ |
85 public function setProxyDumper(ProxyDumper $proxyDumper) | 91 public function setProxyDumper(ProxyDumper $proxyDumper) |
86 { | 92 { |
87 $this->proxyDumper = $proxyDumper; | 93 $this->proxyDumper = $proxyDumper; |
88 } | 94 } |
93 * Available options: | 99 * Available options: |
94 * | 100 * |
95 * * class: The class name | 101 * * class: The class name |
96 * * base_class: The base class name | 102 * * base_class: The base class name |
97 * * namespace: The class namespace | 103 * * namespace: The class namespace |
98 * | 104 * * as_files: To split the container in several files |
99 * @param array $options An array of options | 105 * |
100 * | 106 * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set |
101 * @return string A PHP class representing of the service container | |
102 * | 107 * |
103 * @throws EnvParameterException When an env var exists but has not been dumped | 108 * @throws EnvParameterException When an env var exists but has not been dumped |
104 */ | 109 */ |
105 public function dump(array $options = array()) | 110 public function dump(array $options = array()) |
106 { | 111 { |
107 $this->targetDirRegex = null; | 112 $this->targetDirRegex = null; |
113 $this->inlinedRequires = array(); | |
108 $options = array_merge(array( | 114 $options = array_merge(array( |
109 'class' => 'ProjectServiceContainer', | 115 'class' => 'ProjectServiceContainer', |
110 'base_class' => 'Container', | 116 'base_class' => 'Container', |
111 'namespace' => '', | 117 'namespace' => '', |
118 'as_files' => false, | |
112 'debug' => true, | 119 'debug' => true, |
120 'hot_path_tag' => 'container.hot_path', | |
121 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', | |
122 'build_time' => time(), | |
113 ), $options); | 123 ), $options); |
114 | 124 |
115 $this->initializeMethodNamesMap($options['base_class']); | 125 $this->namespace = $options['namespace']; |
126 $this->asFiles = $options['as_files']; | |
127 $this->hotPathTag = $options['hot_path_tag']; | |
128 $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']); | |
129 | |
130 if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { | |
131 $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); | |
132 $baseClassWithNamespace = $baseClass; | |
133 } elseif ('Container' === $baseClass) { | |
134 $baseClassWithNamespace = Container::class; | |
135 } else { | |
136 $baseClassWithNamespace = $baseClass; | |
137 } | |
138 | |
139 $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); | |
140 | |
141 (new AnalyzeServiceReferencesPass())->process($this->container); | |
142 $this->circularReferences = array(); | |
143 $checkedNodes = array(); | |
144 foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { | |
145 $currentPath = array($id => $id); | |
146 $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); | |
147 } | |
148 $this->container->getCompiler()->getServiceReferenceGraph()->clear(); | |
116 | 149 |
117 $this->docStar = $options['debug'] ? '*' : ''; | 150 $this->docStar = $options['debug'] ? '*' : ''; |
118 | 151 |
119 if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) { | 152 if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) { |
120 // Build a regexp where the first root dirs are mandatory, | 153 // Build a regexp where the first root dirs are mandatory, |
139 | 172 |
140 $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; | 173 $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; |
141 } | 174 } |
142 } | 175 } |
143 | 176 |
144 $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']); | 177 $code = |
145 | 178 $this->startClass($options['class'], $baseClass, $baseClassWithNamespace). |
146 if ($this->container->isFrozen()) { | |
147 $code .= $this->addFrozenConstructor(); | |
148 $code .= $this->addFrozenCompile(); | |
149 $code .= $this->addIsFrozenMethod(); | |
150 } else { | |
151 $code .= $this->addConstructor(); | |
152 } | |
153 | |
154 $code .= | |
155 $this->addServices(). | 179 $this->addServices(). |
156 $this->addDefaultParametersMethod(). | 180 $this->addDefaultParametersMethod(). |
157 $this->endClass(). | 181 $this->endClass() |
158 $this->addProxyClasses() | |
159 ; | 182 ; |
183 | |
184 if ($this->asFiles) { | |
185 $fileStart = <<<EOF | |
186 <?php | |
187 | |
188 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; | |
189 | |
190 // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. | |
191 | |
192 EOF; | |
193 $files = array(); | |
194 | |
195 if ($ids = array_keys($this->container->getRemovedIds())) { | |
196 sort($ids); | |
197 $c = "<?php\n\nreturn array(\n"; | |
198 foreach ($ids as $id) { | |
199 $c .= ' '.$this->doExport($id)." => true,\n"; | |
200 } | |
201 $files['removed-ids.php'] = $c .= ");\n"; | |
202 } | |
203 | |
204 foreach ($this->generateServiceFiles() as $file => $c) { | |
205 $files[$file] = $fileStart.$c; | |
206 } | |
207 foreach ($this->generateProxyClasses() as $file => $c) { | |
208 $files[$file] = "<?php\n".$c; | |
209 } | |
210 $files[$options['class'].'.php'] = $code; | |
211 $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx')); | |
212 $code = array(); | |
213 | |
214 foreach ($files as $file => $c) { | |
215 $code["Container{$hash}/{$file}"] = $c; | |
216 } | |
217 array_pop($code); | |
218 $code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "<?php\n\nnamespace Container{$hash};\n", 0, 6); | |
219 $namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; | |
220 $time = $options['build_time']; | |
221 $id = hash('crc32', $hash.$time); | |
222 | |
223 $code[$options['class'].'.php'] = <<<EOF | |
224 <?php | |
225 {$namespaceLine} | |
226 // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. | |
227 | |
228 if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { | |
229 // no-op | |
230 } elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') { | |
231 touch(__DIR__.'/Container{$hash}.legacy'); | |
232 | |
233 return; | |
234 } | |
235 | |
236 if (!\\class_exists({$options['class']}::class, false)) { | |
237 \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); | |
238 } | |
239 | |
240 return new \\Container{$hash}\\{$options['class']}(array( | |
241 'container.build_hash' => '$hash', | |
242 'container.build_id' => '$id', | |
243 'container.build_time' => $time, | |
244 ), __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); | |
245 | |
246 EOF; | |
247 } else { | |
248 foreach ($this->generateProxyClasses() as $c) { | |
249 $code .= $c; | |
250 } | |
251 } | |
252 | |
160 $this->targetDirRegex = null; | 253 $this->targetDirRegex = null; |
254 $this->inlinedRequires = array(); | |
255 $this->circularReferences = array(); | |
161 | 256 |
162 $unusedEnvs = array(); | 257 $unusedEnvs = array(); |
163 foreach ($this->container->getEnvCounters() as $env => $use) { | 258 foreach ($this->container->getEnvCounters() as $env => $use) { |
164 if (!$use) { | 259 if (!$use) { |
165 $unusedEnvs[] = $env; | 260 $unusedEnvs[] = $env; |
187 } | 282 } |
188 | 283 |
189 /** | 284 /** |
190 * Generates Service local temp variables. | 285 * Generates Service local temp variables. |
191 * | 286 * |
192 * @param string $cId | 287 * @return string |
193 * @param string $definition | 288 */ |
194 * | 289 private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions) |
195 * @return string | 290 { |
196 */ | 291 $allCalls = $calls = $behavior = array(); |
197 private function addServiceLocalTempVariables($cId, $definition) | 292 |
198 { | 293 foreach ($allInlinedDefinitions as $def) { |
199 static $template = " \$%s = %s;\n"; | 294 $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); |
200 | 295 $this->getServiceCallsFromArguments($arguments, $allCalls, false, $cId, $behavior, $allInlinedDefinitions[$def]); |
201 $localDefinitions = array_merge( | 296 } |
202 array($definition), | 297 |
203 $this->getInlinedDefinitions($definition) | 298 $isPreInstance = isset($inlinedDefinitions[$definition]) && isset($this->circularReferences[$cId]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared(); |
204 ); | 299 foreach ($inlinedDefinitions as $def) { |
205 | 300 $this->getServiceCallsFromArguments(array($def->getArguments(), $def->getFactory()), $calls, $isPreInstance, $cId); |
206 $calls = $behavior = array(); | 301 if ($def !== $definition) { |
207 foreach ($localDefinitions as $iDefinition) { | 302 $arguments = array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); |
208 $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior); | 303 $this->getServiceCallsFromArguments($arguments, $calls, $isPreInstance && !$this->hasReference($cId, $arguments, true), $cId); |
209 $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior); | 304 } |
210 $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior); | 305 } |
211 $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior); | 306 if (!isset($inlinedDefinitions[$definition])) { |
212 $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior); | 307 $arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator()); |
308 $this->getServiceCallsFromArguments($arguments, $calls, false, $cId); | |
213 } | 309 } |
214 | 310 |
215 $code = ''; | 311 $code = ''; |
216 foreach ($calls as $id => $callCount) { | 312 foreach ($calls as $id => $callCount) { |
217 if ('service_container' === $id || $id === $cId) { | 313 if ('service_container' === $id || $id === $cId || isset($this->referenceVariables[$id])) { |
218 continue; | 314 continue; |
219 } | 315 } |
220 | 316 if ($callCount <= 1 && $allCalls[$id] <= 1) { |
221 if ($callCount > 1) { | 317 continue; |
222 $name = $this->getNextVariableName(); | 318 } |
223 $this->referenceVariables[$id] = new Variable($name); | 319 |
224 | 320 $name = $this->getNextVariableName(); |
225 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) { | 321 $this->referenceVariables[$id] = new Variable($name); |
226 $code .= sprintf($template, $name, $this->getServiceCall($id)); | 322 |
227 } else { | 323 $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id] ? new Reference($id, $behavior[$id]) : null; |
228 $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE))); | 324 $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($id, $reference)); |
229 } | |
230 } | |
231 } | 325 } |
232 | 326 |
233 if ('' !== $code) { | 327 if ('' !== $code) { |
328 if ($isPreInstance) { | |
329 $code .= <<<EOTXT | |
330 | |
331 if (isset(\$this->services['$cId'])) { | |
332 return \$this->services['$cId']; | |
333 } | |
334 | |
335 EOTXT; | |
336 } | |
337 | |
234 $code .= "\n"; | 338 $code .= "\n"; |
235 } | 339 } |
236 | 340 |
237 return $code; | 341 return $code; |
238 } | 342 } |
239 | 343 |
240 /** | 344 private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath) |
241 * Generates code for the proxies to be attached after the container class. | 345 { |
242 * | 346 foreach ($edges as $edge) { |
243 * @return string | 347 $node = $edge->getDestNode(); |
244 */ | 348 $id = $node->getId(); |
245 private function addProxyClasses() | 349 |
246 { | 350 if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { |
247 /* @var $definitions Definition[] */ | 351 // no-op |
248 $definitions = array_filter( | 352 } elseif (isset($currentPath[$id])) { |
249 $this->container->getDefinitions(), | 353 foreach (array_reverse($currentPath) as $parentId) { |
250 array($this->getProxyDumper(), 'isProxyCandidate') | 354 $this->circularReferences[$parentId][$id] = $id; |
251 ); | 355 $id = $parentId; |
252 $code = ''; | 356 } |
357 } elseif (!isset($checkedNodes[$id])) { | |
358 $checkedNodes[$id] = true; | |
359 $currentPath[$id] = $id; | |
360 $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); | |
361 unset($currentPath[$id]); | |
362 } | |
363 } | |
364 } | |
365 | |
366 private function collectLineage($class, array &$lineage) | |
367 { | |
368 if (isset($lineage[$class])) { | |
369 return; | |
370 } | |
371 if (!$r = $this->container->getReflectionClass($class, false)) { | |
372 return; | |
373 } | |
374 if ($this->container instanceof $class) { | |
375 return; | |
376 } | |
377 $file = $r->getFileName(); | |
378 if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) { | |
379 return; | |
380 } | |
381 | |
382 if ($parent = $r->getParentClass()) { | |
383 $this->collectLineage($parent->name, $lineage); | |
384 } | |
385 | |
386 foreach ($r->getInterfaces() as $parent) { | |
387 $this->collectLineage($parent->name, $lineage); | |
388 } | |
389 | |
390 foreach ($r->getTraits() as $parent) { | |
391 $this->collectLineage($parent->name, $lineage); | |
392 } | |
393 | |
394 $lineage[$class] = substr($exportedFile, 1, -1); | |
395 } | |
396 | |
397 private function generateProxyClasses() | |
398 { | |
399 $definitions = $this->container->getDefinitions(); | |
253 $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); | 400 $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); |
254 | 401 $proxyDumper = $this->getProxyDumper(); |
402 ksort($definitions); | |
255 foreach ($definitions as $definition) { | 403 foreach ($definitions as $definition) { |
256 $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition); | 404 if (!$proxyDumper->isProxyCandidate($definition)) { |
405 continue; | |
406 } | |
407 // register class' reflector for resource tracking | |
408 $this->container->getReflectionClass($definition->getClass()); | |
409 $proxyCode = "\n".$proxyDumper->getProxyCode($definition); | |
257 if ($strip) { | 410 if ($strip) { |
258 $proxyCode = "<?php\n".$proxyCode; | 411 $proxyCode = "<?php\n".$proxyCode; |
259 $proxyCode = substr(Kernel::stripComments($proxyCode), 5); | 412 $proxyCode = substr(Kernel::stripComments($proxyCode), 5); |
260 } | 413 } |
261 $code .= $proxyCode; | 414 yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $proxyCode; |
262 } | 415 } |
263 | |
264 return $code; | |
265 } | 416 } |
266 | 417 |
267 /** | 418 /** |
268 * Generates the require_once statement for service includes. | 419 * Generates the require_once statement for service includes. |
269 * | 420 * |
270 * @param Definition $definition | 421 * @return string |
271 * | 422 */ |
272 * @return string | 423 private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions) |
273 */ | 424 { |
274 private function addServiceInclude($definition) | |
275 { | |
276 $template = " require_once %s;\n"; | |
277 $code = ''; | 425 $code = ''; |
278 | 426 |
279 if (null !== $file = $definition->getFile()) { | 427 if ($this->inlineRequires && !$this->isHotPath($definition)) { |
280 $code .= sprintf($template, $this->dumpValue($file)); | 428 $lineage = $calls = $behavior = array(); |
281 } | 429 foreach ($inlinedDefinitions as $def) { |
282 | 430 if (!$def->isDeprecated() && is_string($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass())) { |
283 foreach ($this->getInlinedDefinitions($definition) as $definition) { | 431 $this->collectLineage($class, $lineage); |
284 if (null !== $file = $definition->getFile()) { | 432 } |
285 $code .= sprintf($template, $this->dumpValue($file)); | 433 $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); |
434 $this->getServiceCallsFromArguments($arguments, $calls, false, $cId, $behavior, $inlinedDefinitions[$def]); | |
435 } | |
436 | |
437 foreach ($calls as $id => $callCount) { | |
438 if ('service_container' !== $id && $id !== $cId | |
439 && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior[$id] | |
440 && $this->container->has($id) | |
441 && $this->isTrivialInstance($def = $this->container->findDefinition($id)) | |
442 && is_string($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass()) | |
443 ) { | |
444 $this->collectLineage($class, $lineage); | |
445 } | |
446 } | |
447 | |
448 foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { | |
449 $code .= sprintf(" include_once %s;\n", $file); | |
450 } | |
451 } | |
452 | |
453 foreach ($inlinedDefinitions as $def) { | |
454 if ($file = $def->getFile()) { | |
455 $code .= sprintf(" include_once %s;\n", $this->dumpValue($file)); | |
286 } | 456 } |
287 } | 457 } |
288 | 458 |
289 if ('' !== $code) { | 459 if ('' !== $code) { |
290 $code .= "\n"; | 460 $code .= "\n"; |
294 } | 464 } |
295 | 465 |
296 /** | 466 /** |
297 * Generates the inline definition of a service. | 467 * Generates the inline definition of a service. |
298 * | 468 * |
469 * @return string | |
470 * | |
471 * @throws RuntimeException When the factory definition is incomplete | |
472 * @throws ServiceCircularReferenceException When a circular reference is detected | |
473 */ | |
474 private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance) | |
475 { | |
476 $code = ''; | |
477 | |
478 foreach ($inlinedDefinitions as $def) { | |
479 if ($definition === $def) { | |
480 continue; | |
481 } | |
482 if ($inlinedDefinitions[$def] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) { | |
483 continue; | |
484 } | |
485 if (isset($this->definitionVariables[$def])) { | |
486 $name = $this->definitionVariables[$def]; | |
487 } else { | |
488 $name = $this->getNextVariableName(); | |
489 $this->definitionVariables[$def] = new Variable($name); | |
490 } | |
491 | |
492 // a construct like: | |
493 // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); | |
494 // this is an indication for a wrong implementation, you can circumvent this problem | |
495 // by setting up your service structure like this: | |
496 // $b = new ServiceB(); | |
497 // $a = new ServiceA(ServiceB $b); | |
498 // $b->setServiceA(ServiceA $a); | |
499 if (isset($inlinedDefinition[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { | |
500 throw new ServiceCircularReferenceException($id, array($id)); | |
501 } | |
502 | |
503 $code .= $this->addNewInstance($def, '$'.$name, ' = ', $id); | |
504 | |
505 if (!$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { | |
506 $code .= $this->addServiceProperties($def, $name); | |
507 $code .= $this->addServiceMethodCalls($def, $name); | |
508 $code .= $this->addServiceConfigurator($def, $name); | |
509 } else { | |
510 $isSimpleInstance = false; | |
511 } | |
512 | |
513 $code .= "\n"; | |
514 } | |
515 | |
516 return $code; | |
517 } | |
518 | |
519 /** | |
520 * Generates the service instance. | |
521 * | |
299 * @param string $id | 522 * @param string $id |
300 * @param Definition $definition | 523 * @param Definition $definition |
301 * | 524 * @param bool $isSimpleInstance |
302 * @return string | |
303 * | |
304 * @throws RuntimeException When the factory definition is incomplete | |
305 * @throws ServiceCircularReferenceException When a circular reference is detected | |
306 */ | |
307 private function addServiceInlinedDefinitions($id, $definition) | |
308 { | |
309 $code = ''; | |
310 $variableMap = $this->definitionVariables; | |
311 $nbOccurrences = new \SplObjectStorage(); | |
312 $processed = new \SplObjectStorage(); | |
313 $inlinedDefinitions = $this->getInlinedDefinitions($definition); | |
314 | |
315 foreach ($inlinedDefinitions as $definition) { | |
316 if (false === $nbOccurrences->contains($definition)) { | |
317 $nbOccurrences->offsetSet($definition, 1); | |
318 } else { | |
319 $i = $nbOccurrences->offsetGet($definition); | |
320 $nbOccurrences->offsetSet($definition, $i + 1); | |
321 } | |
322 } | |
323 | |
324 foreach ($inlinedDefinitions as $sDefinition) { | |
325 if ($processed->contains($sDefinition)) { | |
326 continue; | |
327 } | |
328 $processed->offsetSet($sDefinition); | |
329 | |
330 $class = $this->dumpValue($sDefinition->getClass()); | |
331 if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { | |
332 $name = $this->getNextVariableName(); | |
333 $variableMap->offsetSet($sDefinition, new Variable($name)); | |
334 | |
335 // a construct like: | |
336 // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); | |
337 // this is an indication for a wrong implementation, you can circumvent this problem | |
338 // by setting up your service structure like this: | |
339 // $b = new ServiceB(); | |
340 // $a = new ServiceA(ServiceB $b); | |
341 // $b->setServiceA(ServiceA $a); | |
342 if ($this->hasReference($id, $sDefinition->getArguments())) { | |
343 throw new ServiceCircularReferenceException($id, array($id)); | |
344 } | |
345 | |
346 $code .= $this->addNewInstance($sDefinition, '$'.$name, ' = ', $id); | |
347 | |
348 if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) { | |
349 $code .= $this->addServiceProperties($sDefinition, $name); | |
350 $code .= $this->addServiceMethodCalls($sDefinition, $name); | |
351 $code .= $this->addServiceConfigurator($sDefinition, $name); | |
352 } | |
353 | |
354 $code .= "\n"; | |
355 } | |
356 } | |
357 | |
358 return $code; | |
359 } | |
360 | |
361 /** | |
362 * Adds the service return statement. | |
363 * | |
364 * @param string $id Service id | |
365 * @param Definition $definition | |
366 * | |
367 * @return string | |
368 */ | |
369 private function addServiceReturn($id, $definition) | |
370 { | |
371 if ($this->isSimpleInstance($id, $definition)) { | |
372 return " }\n"; | |
373 } | |
374 | |
375 return "\n return \$instance;\n }\n"; | |
376 } | |
377 | |
378 /** | |
379 * Generates the service instance. | |
380 * | |
381 * @param string $id | |
382 * @param Definition $definition | |
383 * | 525 * |
384 * @return string | 526 * @return string |
385 * | 527 * |
386 * @throws InvalidArgumentException | 528 * @throws InvalidArgumentException |
387 * @throws RuntimeException | 529 * @throws RuntimeException |
388 */ | 530 */ |
389 private function addServiceInstance($id, Definition $definition) | 531 private function addServiceInstance($id, Definition $definition, $isSimpleInstance) |
390 { | 532 { |
391 $class = $this->dumpValue($definition->getClass()); | 533 $class = $this->dumpValue($definition->getClass()); |
392 | 534 |
393 if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { | 535 if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { |
394 throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); | 536 throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); |
395 } | 537 } |
396 | 538 |
397 $simple = $this->isSimpleInstance($id, $definition); | |
398 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); | 539 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); |
399 $instantiation = ''; | 540 $instantiation = ''; |
400 | 541 |
401 if (!$isProxyCandidate && $definition->isShared()) { | 542 if (!$isProxyCandidate && $definition->isShared()) { |
402 $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); | 543 $instantiation = "\$this->services['$id'] = ".($isSimpleInstance ? '' : '$instance'); |
403 } elseif (!$simple) { | 544 } elseif (!$isSimpleInstance) { |
404 $instantiation = '$instance'; | 545 $instantiation = '$instance'; |
405 } | 546 } |
406 | 547 |
407 $return = ''; | 548 $return = ''; |
408 if ($simple) { | 549 if ($isSimpleInstance) { |
409 $return = 'return '; | 550 $return = 'return '; |
410 } else { | 551 } else { |
411 $instantiation .= ' = '; | 552 $instantiation .= ' = '; |
412 } | 553 } |
413 | 554 |
414 $code = $this->addNewInstance($definition, $return, $instantiation, $id); | 555 $code = $this->addNewInstance($definition, $return, $instantiation, $id); |
415 | 556 |
416 if (!$simple) { | 557 if (!$isSimpleInstance) { |
417 $code .= "\n"; | 558 $code .= "\n"; |
418 } | 559 } |
419 | 560 |
420 return $code; | 561 return $code; |
421 } | 562 } |
422 | 563 |
423 /** | 564 /** |
424 * Checks if the definition is a simple instance. | 565 * Checks if the definition is a trivial instance. |
425 * | 566 * |
426 * @param string $id | |
427 * @param Definition $definition | 567 * @param Definition $definition |
428 * | 568 * |
429 * @return bool | 569 * @return bool |
430 */ | 570 */ |
431 private function isSimpleInstance($id, Definition $definition) | 571 private function isTrivialInstance(Definition $definition) |
432 { | 572 { |
433 foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) { | 573 if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) { |
434 if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) { | 574 return false; |
575 } | |
576 if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < count($definition->getArguments())) { | |
577 return false; | |
578 } | |
579 | |
580 foreach ($definition->getArguments() as $arg) { | |
581 if (!$arg || $arg instanceof Parameter) { | |
435 continue; | 582 continue; |
436 } | 583 } |
437 | 584 if (is_array($arg) && 3 >= count($arg)) { |
438 if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) { | 585 foreach ($arg as $k => $v) { |
586 if ($this->dumpValue($k) !== $this->dumpValue($k, false)) { | |
587 return false; | |
588 } | |
589 if (!$v || $v instanceof Parameter) { | |
590 continue; | |
591 } | |
592 if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) { | |
593 continue; | |
594 } | |
595 if (!is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) { | |
596 return false; | |
597 } | |
598 } | |
599 } elseif ($arg instanceof Reference && $this->container->has($id = (string) $arg) && $this->container->findDefinition($id)->isSynthetic()) { | |
600 continue; | |
601 } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) { | |
439 return false; | 602 return false; |
440 } | 603 } |
604 } | |
605 | |
606 if (false !== strpos($this->dumpLiteralClass($this->dumpValue($definition->getClass())), '$')) { | |
607 return false; | |
441 } | 608 } |
442 | 609 |
443 return true; | 610 return true; |
444 } | 611 } |
445 | 612 |
477 } | 644 } |
478 | 645 |
479 /** | 646 /** |
480 * Generates the inline definition setup. | 647 * Generates the inline definition setup. |
481 * | 648 * |
482 * @param string $id | |
483 * @param Definition $definition | |
484 * | |
485 * @return string | 649 * @return string |
486 * | 650 * |
487 * @throws ServiceCircularReferenceException when the container contains a circular reference | 651 * @throws ServiceCircularReferenceException when the container contains a circular reference |
488 */ | 652 */ |
489 private function addServiceInlinedDefinitionsSetup($id, Definition $definition) | 653 private function addServiceInlinedDefinitionsSetup($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, $isSimpleInstance) |
490 { | 654 { |
491 $this->referenceVariables[$id] = new Variable('instance'); | 655 $this->referenceVariables[$id] = new Variable('instance'); |
492 | 656 |
493 $code = ''; | 657 $code = ''; |
494 $processed = new \SplObjectStorage(); | 658 foreach ($inlinedDefinitions as $def) { |
495 foreach ($this->getInlinedDefinitions($definition) as $iDefinition) { | 659 if ($definition === $def || !$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { |
496 if ($processed->contains($iDefinition)) { | |
497 continue; | |
498 } | |
499 $processed->offsetSet($iDefinition); | |
500 | |
501 if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) { | |
502 continue; | 660 continue; |
503 } | 661 } |
504 | 662 |
505 // if the instance is simple, the return statement has already been generated | 663 // if the instance is simple, the return statement has already been generated |
506 // so, the only possible way to get there is because of a circular reference | 664 // so, the only possible way to get there is because of a circular reference |
507 if ($this->isSimpleInstance($id, $definition)) { | 665 if ($isSimpleInstance) { |
508 throw new ServiceCircularReferenceException($id, array($id)); | 666 throw new ServiceCircularReferenceException($id, array($id)); |
509 } | 667 } |
510 | 668 |
511 $name = (string) $this->definitionVariables->offsetGet($iDefinition); | 669 $name = (string) $this->definitionVariables[$def]; |
512 $code .= $this->addServiceProperties($iDefinition, $name); | 670 $code .= $this->addServiceProperties($def, $name); |
513 $code .= $this->addServiceMethodCalls($iDefinition, $name); | 671 $code .= $this->addServiceMethodCalls($def, $name); |
514 $code .= $this->addServiceConfigurator($iDefinition, $name); | 672 $code .= $this->addServiceConfigurator($def, $name); |
515 } | 673 } |
516 | 674 |
517 if ('' !== $code) { | 675 if ('' !== $code && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) { |
518 $code .= "\n"; | 676 $code .= "\n"; |
519 } | 677 } |
520 | 678 |
521 return $code; | 679 return $code; |
522 } | 680 } |
549 | 707 |
550 if (0 === strpos($class, 'new ')) { | 708 if (0 === strpos($class, 'new ')) { |
551 return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); | 709 return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); |
552 } | 710 } |
553 | 711 |
554 return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); | 712 return sprintf(" \\call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); |
555 } | 713 } |
556 | 714 |
557 return sprintf(" %s(\$%s);\n", $callable, $variableName); | 715 return sprintf(" %s(\$%s);\n", $callable, $variableName); |
558 } | 716 } |
559 | 717 |
560 /** | 718 /** |
561 * Adds a service. | 719 * Adds a service. |
562 * | 720 * |
563 * @param string $id | 721 * @param string $id |
564 * @param Definition $definition | 722 * @param Definition $definition |
565 * | 723 * @param string &$file |
566 * @return string | 724 * |
567 */ | 725 * @return string |
568 private function addService($id, Definition $definition) | 726 */ |
727 private function addService($id, Definition $definition, &$file = null) | |
569 { | 728 { |
570 $this->definitionVariables = new \SplObjectStorage(); | 729 $this->definitionVariables = new \SplObjectStorage(); |
571 $this->referenceVariables = array(); | 730 $this->referenceVariables = array(); |
572 $this->variableCount = 0; | 731 $this->variableCount = 0; |
573 | 732 |
574 $return = array(); | 733 $return = array(); |
575 | 734 |
576 if ($definition->isSynthetic()) { | 735 if ($class = $definition->getClass()) { |
577 $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically'; | |
578 } elseif ($class = $definition->getClass()) { | |
579 $class = $this->container->resolveEnvPlaceholders($class); | 736 $class = $this->container->resolveEnvPlaceholders($class); |
580 $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); | 737 $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); |
581 } elseif ($definition->getFactory()) { | 738 } elseif ($definition->getFactory()) { |
582 $factory = $definition->getFactory(); | 739 $factory = $definition->getFactory(); |
583 if (is_string($factory)) { | 740 if (is_string($factory)) { |
610 $lazyInitialization = '$lazyLoad = true'; | 767 $lazyInitialization = '$lazyLoad = true'; |
611 } else { | 768 } else { |
612 $lazyInitialization = ''; | 769 $lazyInitialization = ''; |
613 } | 770 } |
614 | 771 |
615 // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer | 772 $asFile = $this->asFiles && $definition->isShared() && !$this->isHotPath($definition); |
616 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); | |
617 $visibility = $isProxyCandidate ? 'public' : 'protected'; | |
618 $methodName = $this->generateMethodName($id); | 773 $methodName = $this->generateMethodName($id); |
619 $code = <<<EOF | 774 if ($asFile) { |
775 $file = $methodName.'.php'; | |
776 $code = " // Returns the $public '$id'$shared$autowired service.\n\n"; | |
777 } else { | |
778 $code = <<<EOF | |
620 | 779 |
621 /*{$this->docStar} | 780 /*{$this->docStar} |
622 * Gets the $public '$id'$shared$autowired service. | 781 * Gets the $public '$id'$shared$autowired service. |
623 * | 782 * |
624 * $return | 783 * $return |
625 */ | 784 */ |
626 {$visibility} function {$methodName}($lazyInitialization) | 785 protected function {$methodName}($lazyInitialization) |
627 { | 786 { |
628 | 787 |
629 EOF; | 788 EOF; |
630 | 789 } |
631 $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $methodName) : ''; | 790 |
632 | 791 if ($this->getProxyDumper()->isProxyCandidate($definition)) { |
633 if ($definition->isSynthetic()) { | 792 $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)'; |
634 $code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id); | 793 $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName)); |
794 } | |
795 | |
796 if ($definition->isDeprecated()) { | |
797 $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); | |
798 } | |
799 | |
800 $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); | |
801 $constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory())); | |
802 $otherDefinitions = new \SplObjectStorage(); | |
803 | |
804 foreach ($inlinedDefinitions as $def) { | |
805 if ($def === $definition || isset($constructorDefinitions[$def])) { | |
806 $constructorDefinitions[$def] = $inlinedDefinitions[$def]; | |
807 } else { | |
808 $otherDefinitions[$def] = $inlinedDefinitions[$def]; | |
809 } | |
810 } | |
811 | |
812 $isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator(); | |
813 | |
814 $code .= | |
815 $this->addServiceInclude($id, $definition, $inlinedDefinitions). | |
816 $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $inlinedDefinitions). | |
817 $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance). | |
818 $this->addServiceInstance($id, $definition, $isSimpleInstance). | |
819 $this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $inlinedDefinitions). | |
820 $this->addServiceInlinedDefinitions($id, $definition, $otherDefinitions, $isSimpleInstance). | |
821 $this->addServiceInlinedDefinitionsSetup($id, $definition, $inlinedDefinitions, $isSimpleInstance). | |
822 $this->addServiceProperties($definition). | |
823 $this->addServiceMethodCalls($definition). | |
824 $this->addServiceConfigurator($definition). | |
825 (!$isSimpleInstance ? "\n return \$instance;\n" : '') | |
826 ; | |
827 | |
828 if ($asFile) { | |
829 $code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code))); | |
635 } else { | 830 } else { |
636 if ($definition->isDeprecated()) { | 831 $code .= " }\n"; |
637 $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); | |
638 } | |
639 | |
640 $code .= | |
641 $this->addServiceInclude($definition). | |
642 $this->addServiceLocalTempVariables($id, $definition). | |
643 $this->addServiceInlinedDefinitions($id, $definition). | |
644 $this->addServiceInstance($id, $definition). | |
645 $this->addServiceInlinedDefinitionsSetup($id, $definition). | |
646 $this->addServiceProperties($definition). | |
647 $this->addServiceMethodCalls($definition). | |
648 $this->addServiceConfigurator($definition). | |
649 $this->addServiceReturn($id, $definition) | |
650 ; | |
651 } | 832 } |
652 | 833 |
653 $this->definitionVariables = null; | 834 $this->definitionVariables = null; |
654 $this->referenceVariables = null; | 835 $this->referenceVariables = null; |
655 | 836 |
665 { | 846 { |
666 $publicServices = $privateServices = ''; | 847 $publicServices = $privateServices = ''; |
667 $definitions = $this->container->getDefinitions(); | 848 $definitions = $this->container->getDefinitions(); |
668 ksort($definitions); | 849 ksort($definitions); |
669 foreach ($definitions as $id => $definition) { | 850 foreach ($definitions as $id => $definition) { |
851 if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition))) { | |
852 continue; | |
853 } | |
670 if ($definition->isPublic()) { | 854 if ($definition->isPublic()) { |
671 $publicServices .= $this->addService($id, $definition); | 855 $publicServices .= $this->addService($id, $definition); |
672 } else { | 856 } else { |
673 $privateServices .= $this->addService($id, $definition); | 857 $privateServices .= $this->addService($id, $definition); |
674 } | 858 } |
675 } | 859 } |
676 | 860 |
677 return $publicServices.$privateServices; | 861 return $publicServices.$privateServices; |
678 } | 862 } |
679 | 863 |
864 private function generateServiceFiles() | |
865 { | |
866 $definitions = $this->container->getDefinitions(); | |
867 ksort($definitions); | |
868 foreach ($definitions as $id => $definition) { | |
869 if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) { | |
870 $code = $this->addService($id, $definition, $file); | |
871 yield $file => $code; | |
872 } | |
873 } | |
874 } | |
875 | |
680 private function addNewInstance(Definition $definition, $return, $instantiation, $id) | 876 private function addNewInstance(Definition $definition, $return, $instantiation, $id) |
681 { | 877 { |
682 $class = $this->dumpValue($definition->getClass()); | 878 $class = $this->dumpValue($definition->getClass()); |
879 $return = ' '.$return.$instantiation; | |
683 | 880 |
684 $arguments = array(); | 881 $arguments = array(); |
685 foreach ($definition->getArguments() as $value) { | 882 foreach ($definition->getArguments() as $value) { |
686 $arguments[] = $this->dumpValue($value); | 883 $arguments[] = $this->dumpValue($value); |
687 } | 884 } |
693 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); | 890 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); |
694 } | 891 } |
695 | 892 |
696 if ($callable[0] instanceof Reference | 893 if ($callable[0] instanceof Reference |
697 || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { | 894 || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { |
698 return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); | 895 return $return.sprintf("%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); |
699 } | 896 } |
700 | 897 |
701 $class = $this->dumpValue($callable[0]); | 898 $class = $this->dumpValue($callable[0]); |
702 // If the class is a string we can optimize call_user_func away | 899 // If the class is a string we can optimize call_user_func away |
703 if (0 === strpos($class, "'") && false === strpos($class, '$')) { | 900 if (0 === strpos($class, "'") && false === strpos($class, '$')) { |
704 if ("''" === $class) { | 901 if ("''" === $class) { |
705 throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id)); | 902 throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id)); |
706 } | 903 } |
707 | 904 |
708 return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); | 905 return $return.sprintf("%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); |
709 } | 906 } |
710 | 907 |
711 if (0 === strpos($class, 'new ')) { | 908 if (0 === strpos($class, 'new ')) { |
712 return sprintf(" $return{$instantiation}(%s)->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); | 909 return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); |
713 } | 910 } |
714 | 911 |
715 return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); | 912 return $return.sprintf("\\call_user_func(array(%s, '%s')%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); |
716 } | 913 } |
717 | 914 |
718 return sprintf(" $return{$instantiation}%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); | 915 return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); |
719 } | 916 } |
720 | 917 |
721 if (false !== strpos($class, '$')) { | 918 if (false !== strpos($class, '$')) { |
722 return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments)); | 919 return sprintf(" \$class = %s;\n\n%snew \$class(%s);\n", $class, $return, implode(', ', $arguments)); |
723 } | 920 } |
724 | 921 |
725 return sprintf(" $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); | 922 return $return.sprintf("new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); |
726 } | 923 } |
727 | 924 |
728 /** | 925 /** |
729 * Adds the class headers. | 926 * Adds the class headers. |
730 * | 927 * |
731 * @param string $class Class name | 928 * @param string $class Class name |
732 * @param string $baseClass The name of the base class | 929 * @param string $baseClass The name of the base class |
733 * @param string $namespace The class namespace | 930 * @param string $baseClassWithNamespace Fully qualified base class name |
734 * | 931 * |
735 * @return string | 932 * @return string |
736 */ | 933 */ |
737 private function startClass($class, $baseClass, $namespace) | 934 private function startClass($class, $baseClass, $baseClassWithNamespace) |
738 { | 935 { |
739 $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; | 936 $bagClass = $this->container->isCompiled() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; |
740 $namespaceLine = $namespace ? "\nnamespace $namespace;\n" : ''; | 937 $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; |
741 | 938 |
742 return <<<EOF | 939 $code = <<<EOF |
743 <?php | 940 <?php |
744 $namespaceLine | 941 $namespaceLine |
942 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; | |
745 use Symfony\Component\DependencyInjection\ContainerInterface; | 943 use Symfony\Component\DependencyInjection\ContainerInterface; |
746 use Symfony\Component\DependencyInjection\Container; | 944 use Symfony\Component\DependencyInjection\Container; |
747 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; | 945 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; |
748 use Symfony\Component\DependencyInjection\Exception\LogicException; | 946 use Symfony\Component\DependencyInjection\Exception\LogicException; |
749 use Symfony\Component\DependencyInjection\Exception\RuntimeException; | 947 use Symfony\Component\DependencyInjection\Exception\RuntimeException; |
750 $bagClass | 948 $bagClass |
751 | 949 |
752 /*{$this->docStar} | 950 /*{$this->docStar} |
753 * $class. | |
754 * | |
755 * This class has been auto-generated | 951 * This class has been auto-generated |
756 * by the Symfony Dependency Injection Component. | 952 * by the Symfony Dependency Injection Component. |
953 * | |
954 * @final since Symfony 3.3 | |
757 */ | 955 */ |
758 class $class extends $baseClass | 956 class $class extends $baseClass |
759 { | 957 { |
760 private \$parameters; | 958 private \$parameters; |
761 private \$targetDirs = array(); | 959 private \$targetDirs = array(); |
762 | 960 |
961 public function __construct() | |
962 { | |
963 | |
763 EOF; | 964 EOF; |
764 } | 965 if (null !== $this->targetDirRegex) { |
765 | 966 $dir = $this->asFiles ? '$this->targetDirs[0] = \\dirname($containerDir)' : '__DIR__'; |
766 /** | 967 $code .= <<<EOF |
767 * Adds the constructor. | 968 \$dir = {$dir}; |
768 * | 969 for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) { |
769 * @return string | 970 \$this->targetDirs[\$i] = \$dir = \\dirname(\$dir); |
770 */ | 971 } |
771 private function addConstructor() | |
772 { | |
773 $targetDirs = $this->exportTargetDirs(); | |
774 $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; | |
775 | |
776 $code = <<<EOF | |
777 | |
778 /*{$this->docStar} | |
779 * Constructor. | |
780 */ | |
781 public function __construct() | |
782 {{$targetDirs} | |
783 parent::__construct($arguments); | |
784 | 972 |
785 EOF; | 973 EOF; |
786 | 974 } |
975 if ($this->asFiles) { | |
976 $code = str_replace('$parameters', "\$buildParameters;\n private \$containerDir;\n private \$parameters", $code); | |
977 $code = str_replace('__construct()', '__construct(array $buildParameters = array(), $containerDir = __DIR__)', $code); | |
978 $code .= " \$this->buildParameters = \$buildParameters;\n"; | |
979 $code .= " \$this->containerDir = \$containerDir;\n"; | |
980 } | |
981 | |
982 if ($this->container->isCompiled()) { | |
983 if (Container::class !== $baseClassWithNamespace) { | |
984 $r = $this->container->getReflectionClass($baseClassWithNamespace, false); | |
985 if (null !== $r | |
986 && (null !== $constructor = $r->getConstructor()) | |
987 && 0 === $constructor->getNumberOfRequiredParameters() | |
988 && Container::class !== $constructor->getDeclaringClass()->name | |
989 ) { | |
990 $code .= " parent::__construct();\n"; | |
991 $code .= " \$this->parameterBag = null;\n\n"; | |
992 } | |
993 } | |
994 | |
995 if ($this->container->getParameterBag()->all()) { | |
996 $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; | |
997 } | |
998 | |
999 $code .= " \$this->services = array();\n"; | |
1000 } else { | |
1001 $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; | |
1002 $code .= " parent::__construct($arguments);\n"; | |
1003 } | |
1004 | |
1005 $code .= $this->addNormalizedIds(); | |
1006 $code .= $this->addSyntheticIds(); | |
787 $code .= $this->addMethodMap(); | 1007 $code .= $this->addMethodMap(); |
1008 $code .= $this->asFiles ? $this->addFileMap() : ''; | |
788 $code .= $this->addPrivateServices(); | 1009 $code .= $this->addPrivateServices(); |
789 $code .= $this->addAliases(); | 1010 $code .= $this->addAliases(); |
790 | 1011 $code .= $this->addInlineRequires(); |
791 $code .= <<<'EOF' | 1012 $code .= <<<'EOF' |
792 } | 1013 } |
793 | 1014 |
794 EOF; | 1015 EOF; |
1016 $code .= $this->addRemovedIds(); | |
1017 | |
1018 if ($this->container->isCompiled()) { | |
1019 $code .= <<<EOF | |
1020 | |
1021 public function compile() | |
1022 { | |
1023 throw new LogicException('You cannot compile a dumped container that was already compiled.'); | |
1024 } | |
1025 | |
1026 public function isCompiled() | |
1027 { | |
1028 return true; | |
1029 } | |
1030 | |
1031 public function isFrozen() | |
1032 { | |
1033 @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); | |
1034 | |
1035 return true; | |
1036 } | |
1037 | |
1038 EOF; | |
1039 } | |
1040 | |
1041 if ($this->asFiles) { | |
1042 $code .= <<<EOF | |
1043 | |
1044 protected function load(\$file, \$lazyLoad = true) | |
1045 { | |
1046 return require \$this->containerDir.\\DIRECTORY_SEPARATOR.\$file; | |
1047 } | |
1048 | |
1049 EOF; | |
1050 } | |
1051 | |
1052 $proxyDumper = $this->getProxyDumper(); | |
1053 foreach ($this->container->getDefinitions() as $definition) { | |
1054 if (!$proxyDumper->isProxyCandidate($definition)) { | |
1055 continue; | |
1056 } | |
1057 if ($this->asFiles) { | |
1058 $proxyLoader = '$this->load("{$class}.php")'; | |
1059 } elseif ($this->namespace) { | |
1060 $proxyLoader = 'class_alias("'.$this->namespace.'\\\\{$class}", $class, false)'; | |
1061 } else { | |
1062 $proxyLoader = ''; | |
1063 } | |
1064 if ($proxyLoader) { | |
1065 $proxyLoader = "class_exists(\$class, false) || {$proxyLoader};\n\n "; | |
1066 } | |
1067 $code .= <<<EOF | |
1068 | |
1069 protected function createProxy(\$class, \Closure \$factory) | |
1070 { | |
1071 {$proxyLoader}return \$factory(); | |
1072 } | |
1073 | |
1074 EOF; | |
1075 break; | |
1076 } | |
795 | 1077 |
796 return $code; | 1078 return $code; |
797 } | 1079 } |
798 | 1080 |
799 /** | 1081 /** |
800 * Adds the constructor for a frozen container. | 1082 * Adds the normalizedIds property definition. |
801 * | 1083 * |
802 * @return string | 1084 * @return string |
803 */ | 1085 */ |
804 private function addFrozenConstructor() | 1086 private function addNormalizedIds() |
805 { | 1087 { |
806 $targetDirs = $this->exportTargetDirs(); | 1088 $code = ''; |
807 | 1089 $normalizedIds = $this->container->getNormalizedIds(); |
808 $code = <<<EOF | 1090 ksort($normalizedIds); |
809 | 1091 foreach ($normalizedIds as $id => $normalizedId) { |
810 /*{$this->docStar} | 1092 if ($this->container->has($normalizedId)) { |
811 * Constructor. | 1093 $code .= ' '.$this->doExport($id).' => '.$this->doExport($normalizedId).",\n"; |
812 */ | 1094 } |
813 public function __construct() | 1095 } |
814 {{$targetDirs} | 1096 |
815 EOF; | 1097 return $code ? " \$this->normalizedIds = array(\n".$code." );\n" : ''; |
816 | 1098 } |
817 if ($this->container->getParameterBag()->all()) { | 1099 |
818 $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; | 1100 /** |
819 } | 1101 * Adds the syntheticIds definition. |
820 | 1102 * |
821 $code .= "\n \$this->services = array();\n"; | 1103 * @return string |
822 $code .= $this->addMethodMap(); | 1104 */ |
823 $code .= $this->addPrivateServices(); | 1105 private function addSyntheticIds() |
824 $code .= $this->addAliases(); | 1106 { |
825 | 1107 $code = ''; |
826 $code .= <<<'EOF' | 1108 $definitions = $this->container->getDefinitions(); |
827 } | |
828 | |
829 EOF; | |
830 | |
831 return $code; | |
832 } | |
833 | |
834 /** | |
835 * Adds the constructor for a frozen container. | |
836 * | |
837 * @return string | |
838 */ | |
839 private function addFrozenCompile() | |
840 { | |
841 return <<<EOF | |
842 | |
843 /*{$this->docStar} | |
844 * {@inheritdoc} | |
845 */ | |
846 public function compile() | |
847 { | |
848 throw new LogicException('You cannot compile a dumped frozen container.'); | |
849 } | |
850 | |
851 EOF; | |
852 } | |
853 | |
854 /** | |
855 * Adds the isFrozen method for a frozen container. | |
856 * | |
857 * @return string | |
858 */ | |
859 private function addIsFrozenMethod() | |
860 { | |
861 return <<<EOF | |
862 | |
863 /*{$this->docStar} | |
864 * {@inheritdoc} | |
865 */ | |
866 public function isFrozen() | |
867 { | |
868 return true; | |
869 } | |
870 | |
871 EOF; | |
872 } | |
873 | |
874 /** | |
875 * Adds the methodMap property definition. | |
876 * | |
877 * @return string | |
878 */ | |
879 private function addMethodMap() | |
880 { | |
881 if (!$definitions = $this->container->getDefinitions()) { | |
882 return ''; | |
883 } | |
884 | |
885 $code = " \$this->methodMap = array(\n"; | |
886 ksort($definitions); | 1109 ksort($definitions); |
887 foreach ($definitions as $id => $definition) { | 1110 foreach ($definitions as $id => $definition) { |
888 $code .= ' '.$this->export($id).' => '.$this->export($this->generateMethodName($id)).",\n"; | 1111 if ($definition->isSynthetic() && 'service_container' !== $id) { |
889 } | 1112 $code .= ' '.$this->doExport($id)." => true,\n"; |
890 | 1113 } |
891 return $code." );\n"; | 1114 } |
1115 | |
1116 return $code ? " \$this->syntheticIds = array(\n{$code} );\n" : ''; | |
1117 } | |
1118 | |
1119 /** | |
1120 * Adds the removedIds definition. | |
1121 * | |
1122 * @return string | |
1123 */ | |
1124 private function addRemovedIds() | |
1125 { | |
1126 if (!$ids = $this->container->getRemovedIds()) { | |
1127 return ''; | |
1128 } | |
1129 if ($this->asFiles) { | |
1130 $code = "require \$this->containerDir.\\DIRECTORY_SEPARATOR.'removed-ids.php'"; | |
1131 } else { | |
1132 $code = ''; | |
1133 $ids = array_keys($ids); | |
1134 sort($ids); | |
1135 foreach ($ids as $id) { | |
1136 $code .= ' '.$this->doExport($id)." => true,\n"; | |
1137 } | |
1138 | |
1139 $code = "array(\n{$code} )"; | |
1140 } | |
1141 | |
1142 return <<<EOF | |
1143 | |
1144 public function getRemovedIds() | |
1145 { | |
1146 return {$code}; | |
1147 } | |
1148 | |
1149 EOF; | |
1150 } | |
1151 | |
1152 /** | |
1153 * Adds the methodMap property definition. | |
1154 * | |
1155 * @return string | |
1156 */ | |
1157 private function addMethodMap() | |
1158 { | |
1159 $code = ''; | |
1160 $definitions = $this->container->getDefinitions(); | |
1161 ksort($definitions); | |
1162 foreach ($definitions as $id => $definition) { | |
1163 if (!$definition->isSynthetic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) { | |
1164 $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; | |
1165 } | |
1166 } | |
1167 | |
1168 return $code ? " \$this->methodMap = array(\n{$code} );\n" : ''; | |
1169 } | |
1170 | |
1171 /** | |
1172 * Adds the fileMap property definition. | |
1173 * | |
1174 * @return string | |
1175 */ | |
1176 private function addFileMap() | |
1177 { | |
1178 $code = ''; | |
1179 $definitions = $this->container->getDefinitions(); | |
1180 ksort($definitions); | |
1181 foreach ($definitions as $id => $definition) { | |
1182 if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) { | |
1183 $code .= sprintf(" %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id)); | |
1184 } | |
1185 } | |
1186 | |
1187 return $code ? " \$this->fileMap = array(\n{$code} );\n" : ''; | |
892 } | 1188 } |
893 | 1189 |
894 /** | 1190 /** |
895 * Adds the privates property definition. | 1191 * Adds the privates property definition. |
896 * | 1192 * |
897 * @return string | 1193 * @return string |
898 */ | 1194 */ |
899 private function addPrivateServices() | 1195 private function addPrivateServices() |
900 { | 1196 { |
901 if (!$definitions = $this->container->getDefinitions()) { | |
902 return ''; | |
903 } | |
904 | |
905 $code = ''; | 1197 $code = ''; |
1198 | |
1199 $aliases = $this->container->getAliases(); | |
1200 ksort($aliases); | |
1201 foreach ($aliases as $id => $alias) { | |
1202 if ($alias->isPrivate()) { | |
1203 $code .= ' '.$this->doExport($id)." => true,\n"; | |
1204 } | |
1205 } | |
1206 | |
1207 $definitions = $this->container->getDefinitions(); | |
906 ksort($definitions); | 1208 ksort($definitions); |
907 foreach ($definitions as $id => $definition) { | 1209 foreach ($definitions as $id => $definition) { |
908 if (!$definition->isPublic()) { | 1210 if (!$definition->isPublic()) { |
909 $code .= ' '.$this->export($id)." => true,\n"; | 1211 $code .= ' '.$this->doExport($id)." => true,\n"; |
910 } | 1212 } |
911 } | 1213 } |
912 | 1214 |
913 if (empty($code)) { | 1215 if (empty($code)) { |
914 return ''; | 1216 return ''; |
927 * @return string | 1229 * @return string |
928 */ | 1230 */ |
929 private function addAliases() | 1231 private function addAliases() |
930 { | 1232 { |
931 if (!$aliases = $this->container->getAliases()) { | 1233 if (!$aliases = $this->container->getAliases()) { |
932 return $this->container->isFrozen() ? "\n \$this->aliases = array();\n" : ''; | 1234 return $this->container->isCompiled() ? "\n \$this->aliases = array();\n" : ''; |
933 } | 1235 } |
934 | 1236 |
935 $code = " \$this->aliases = array(\n"; | 1237 $code = " \$this->aliases = array(\n"; |
936 ksort($aliases); | 1238 ksort($aliases); |
937 foreach ($aliases as $alias => $id) { | 1239 foreach ($aliases as $alias => $id) { |
938 $id = (string) $id; | 1240 $id = $this->container->normalizeId($id); |
939 while (isset($aliases[$id])) { | 1241 while (isset($aliases[$id])) { |
940 $id = (string) $aliases[$id]; | 1242 $id = $this->container->normalizeId($aliases[$id]); |
941 } | 1243 } |
942 $code .= ' '.$this->export($alias).' => '.$this->export($id).",\n"; | 1244 $code .= ' '.$this->doExport($alias).' => '.$this->doExport($id).",\n"; |
943 } | 1245 } |
944 | 1246 |
945 return $code." );\n"; | 1247 return $code." );\n"; |
1248 } | |
1249 | |
1250 private function addInlineRequires() | |
1251 { | |
1252 if (!$this->hotPathTag || !$this->inlineRequires) { | |
1253 return ''; | |
1254 } | |
1255 | |
1256 $lineage = array(); | |
1257 | |
1258 foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) { | |
1259 $definition = $this->container->getDefinition($id); | |
1260 $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); | |
1261 | |
1262 foreach ($inlinedDefinitions as $def) { | |
1263 if (is_string($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass())) { | |
1264 $this->collectLineage($class, $lineage); | |
1265 } | |
1266 } | |
1267 } | |
1268 | |
1269 $code = ''; | |
1270 | |
1271 foreach ($lineage as $file) { | |
1272 if (!isset($this->inlinedRequires[$file])) { | |
1273 $this->inlinedRequires[$file] = true; | |
1274 $code .= sprintf("\n include_once %s;", $file); | |
1275 } | |
1276 } | |
1277 | |
1278 return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : ''; | |
946 } | 1279 } |
947 | 1280 |
948 /** | 1281 /** |
949 * Adds default parameters method. | 1282 * Adds default parameters method. |
950 * | 1283 * |
956 return ''; | 1289 return ''; |
957 } | 1290 } |
958 | 1291 |
959 $php = array(); | 1292 $php = array(); |
960 $dynamicPhp = array(); | 1293 $dynamicPhp = array(); |
1294 $normalizedParams = array(); | |
961 | 1295 |
962 foreach ($this->container->getParameterBag()->all() as $key => $value) { | 1296 foreach ($this->container->getParameterBag()->all() as $key => $value) { |
963 if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { | 1297 if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { |
964 throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey)); | 1298 throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey)); |
965 } | 1299 } |
1300 if ($key !== $lcKey = strtolower($key)) { | |
1301 $normalizedParams[] = sprintf(' %s => %s,', $this->export($lcKey), $this->export($key)); | |
1302 } | |
966 $export = $this->exportParameters(array($value)); | 1303 $export = $this->exportParameters(array($value)); |
967 $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2); | 1304 $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2); |
968 | 1305 |
969 if (preg_match("/\\\$this->(?:getEnv\('\w++'\)|targetDirs\[\d++\])/", $export[1])) { | 1306 if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) { |
970 $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]); | 1307 $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]); |
971 } else { | 1308 } else { |
972 $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]); | 1309 $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]); |
973 } | 1310 } |
974 } | 1311 } |
975 $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8)); | 1312 $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8)); |
976 | 1313 |
977 $code = ''; | 1314 $code = ''; |
978 if ($this->container->isFrozen()) { | 1315 if ($this->container->isCompiled()) { |
979 $code .= <<<'EOF' | 1316 $code .= <<<'EOF' |
980 | 1317 |
981 /** | |
982 * {@inheritdoc} | |
983 */ | |
984 public function getParameter($name) | 1318 public function getParameter($name) |
985 { | 1319 { |
986 $name = strtolower($name); | 1320 $name = (string) $name; |
987 | 1321 if (isset($this->buildParameters[$name])) { |
988 if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) { | 1322 return $this->buildParameters[$name]; |
989 throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); | 1323 } |
1324 if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { | |
1325 $name = $this->normalizeParameterName($name); | |
1326 | |
1327 if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { | |
1328 throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); | |
1329 } | |
990 } | 1330 } |
991 if (isset($this->loadedDynamicParameters[$name])) { | 1331 if (isset($this->loadedDynamicParameters[$name])) { |
992 return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); | 1332 return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); |
993 } | 1333 } |
994 | 1334 |
995 return $this->parameters[$name]; | 1335 return $this->parameters[$name]; |
996 } | 1336 } |
997 | 1337 |
998 /** | |
999 * {@inheritdoc} | |
1000 */ | |
1001 public function hasParameter($name) | 1338 public function hasParameter($name) |
1002 { | 1339 { |
1003 $name = strtolower($name); | 1340 $name = (string) $name; |
1004 | 1341 if (isset($this->buildParameters[$name])) { |
1005 return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]); | 1342 return true; |
1006 } | 1343 } |
1007 | 1344 $name = $this->normalizeParameterName($name); |
1008 /** | 1345 |
1009 * {@inheritdoc} | 1346 return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); |
1010 */ | 1347 } |
1348 | |
1011 public function setParameter($name, $value) | 1349 public function setParameter($name, $value) |
1012 { | 1350 { |
1013 throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); | 1351 throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); |
1014 } | 1352 } |
1015 | 1353 |
1016 /** | |
1017 * {@inheritdoc} | |
1018 */ | |
1019 public function getParameterBag() | 1354 public function getParameterBag() |
1020 { | 1355 { |
1021 if (null === $this->parameterBag) { | 1356 if (null === $this->parameterBag) { |
1022 $parameters = $this->parameters; | 1357 $parameters = $this->parameters; |
1023 foreach ($this->loadedDynamicParameters as $name => $loaded) { | 1358 foreach ($this->loadedDynamicParameters as $name => $loaded) { |
1024 $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); | 1359 $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); |
1025 } | 1360 } |
1361 foreach ($this->buildParameters as $name => $value) { | |
1362 $parameters[$name] = $value; | |
1363 } | |
1026 $this->parameterBag = new FrozenParameterBag($parameters); | 1364 $this->parameterBag = new FrozenParameterBag($parameters); |
1027 } | 1365 } |
1028 | 1366 |
1029 return $this->parameterBag; | 1367 return $this->parameterBag; |
1030 } | 1368 } |
1031 | 1369 |
1032 EOF; | 1370 EOF; |
1033 if ('' === $this->docStar) { | 1371 if (!$this->asFiles) { |
1034 $code = str_replace('/**', '/*', $code); | 1372 $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code); |
1035 } | 1373 } |
1036 | 1374 |
1037 if ($dynamicPhp) { | 1375 if ($dynamicPhp) { |
1038 $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); | 1376 $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); |
1039 $getDynamicParameter = <<<'EOF' | 1377 $getDynamicParameter = <<<'EOF' |
1068 private function getDynamicParameter(\$name) | 1406 private function getDynamicParameter(\$name) |
1069 { | 1407 { |
1070 {$getDynamicParameter} | 1408 {$getDynamicParameter} |
1071 } | 1409 } |
1072 | 1410 |
1411 | |
1412 EOF; | |
1413 | |
1414 $code .= ' private $normalizedParameterNames = '.($normalizedParams ? sprintf("array(\n%s\n );", implode("\n", $normalizedParams)) : 'array();')."\n"; | |
1415 $code .= <<<'EOF' | |
1416 | |
1417 private function normalizeParameterName($name) | |
1418 { | |
1419 if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { | |
1420 $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; | |
1421 if ((string) $name !== $normalizedName) { | |
1422 @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); | |
1423 } | |
1424 } else { | |
1425 $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; | |
1426 } | |
1427 | |
1428 return $normalizedName; | |
1429 } | |
1430 | |
1073 EOF; | 1431 EOF; |
1074 } elseif ($dynamicPhp) { | 1432 } elseif ($dynamicPhp) { |
1075 throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.'); | 1433 throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.'); |
1076 } | 1434 } |
1077 | 1435 |
1107 { | 1465 { |
1108 $php = array(); | 1466 $php = array(); |
1109 foreach ($parameters as $key => $value) { | 1467 foreach ($parameters as $key => $value) { |
1110 if (is_array($value)) { | 1468 if (is_array($value)) { |
1111 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); | 1469 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); |
1470 } elseif ($value instanceof ArgumentInterface) { | |
1471 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', get_class($value), $path.'/'.$key)); | |
1112 } elseif ($value instanceof Variable) { | 1472 } elseif ($value instanceof Variable) { |
1113 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); | 1473 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); |
1114 } elseif ($value instanceof Definition) { | 1474 } elseif ($value instanceof Definition) { |
1115 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key)); | 1475 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key)); |
1116 } elseif ($value instanceof Reference) { | 1476 } elseif ($value instanceof Reference) { |
1148 * | 1508 * |
1149 * @return string | 1509 * @return string |
1150 */ | 1510 */ |
1151 private function wrapServiceConditionals($value, $code) | 1511 private function wrapServiceConditionals($value, $code) |
1152 { | 1512 { |
1153 if (!$services = ContainerBuilder::getServiceConditionals($value)) { | 1513 if (!$condition = $this->getServiceConditionals($value)) { |
1154 return $code; | 1514 return $code; |
1155 } | 1515 } |
1156 | 1516 |
1517 // re-indent the wrapped code | |
1518 $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); | |
1519 | |
1520 return sprintf(" if (%s) {\n%s }\n", $condition, $code); | |
1521 } | |
1522 | |
1523 /** | |
1524 * Get the conditions to execute for conditional services. | |
1525 * | |
1526 * @param string $value | |
1527 * | |
1528 * @return null|string | |
1529 */ | |
1530 private function getServiceConditionals($value) | |
1531 { | |
1157 $conditions = array(); | 1532 $conditions = array(); |
1158 foreach ($services as $service) { | 1533 foreach (ContainerBuilder::getInitializedConditionals($value) as $service) { |
1534 if (!$this->container->hasDefinition($service)) { | |
1535 return 'false'; | |
1536 } | |
1537 $conditions[] = sprintf("isset(\$this->services['%s'])", $service); | |
1538 } | |
1539 foreach (ContainerBuilder::getServiceConditionals($value) as $service) { | |
1159 if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { | 1540 if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { |
1160 continue; | 1541 continue; |
1161 } | 1542 } |
1162 | 1543 |
1163 $conditions[] = sprintf("\$this->has('%s')", $service); | 1544 $conditions[] = sprintf("\$this->has('%s')", $service); |
1164 } | 1545 } |
1165 | 1546 |
1166 if (!$conditions) { | 1547 if (!$conditions) { |
1167 return $code; | 1548 return ''; |
1168 } | 1549 } |
1169 | 1550 |
1170 // re-indent the wrapped code | 1551 return implode(' && ', $conditions); |
1171 $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); | |
1172 | |
1173 return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code); | |
1174 } | 1552 } |
1175 | 1553 |
1176 /** | 1554 /** |
1177 * Builds service calls from arguments. | 1555 * Builds service calls from arguments. |
1178 * | 1556 */ |
1179 * @param array $arguments | 1557 private function getServiceCallsFromArguments(array $arguments, array &$calls, $isPreInstance, $callerId, array &$behavior = array(), $step = 1) |
1180 * @param array &$calls By reference | |
1181 * @param array &$behavior By reference | |
1182 */ | |
1183 private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior) | |
1184 { | 1558 { |
1185 foreach ($arguments as $argument) { | 1559 foreach ($arguments as $argument) { |
1186 if (is_array($argument)) { | 1560 if (is_array($argument)) { |
1187 $this->getServiceCallsFromArguments($argument, $calls, $behavior); | 1561 $this->getServiceCallsFromArguments($argument, $calls, $isPreInstance, $callerId, $behavior, $step); |
1188 } elseif ($argument instanceof Reference) { | 1562 } elseif ($argument instanceof Reference) { |
1189 $id = (string) $argument; | 1563 $id = $this->container->normalizeId($argument); |
1190 | 1564 |
1191 if (!isset($calls[$id])) { | 1565 if (!isset($calls[$id])) { |
1192 $calls[$id] = 0; | 1566 $calls[$id] = (int) ($isPreInstance && isset($this->circularReferences[$callerId][$id])); |
1193 } | 1567 } |
1194 if (!isset($behavior[$id])) { | 1568 if (!isset($behavior[$id])) { |
1195 $behavior[$id] = $argument->getInvalidBehavior(); | 1569 $behavior[$id] = $argument->getInvalidBehavior(); |
1196 } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) { | 1570 } else { |
1197 $behavior[$id] = $argument->getInvalidBehavior(); | 1571 $behavior[$id] = min($behavior[$id], $argument->getInvalidBehavior()); |
1198 } | 1572 } |
1199 | 1573 |
1200 ++$calls[$id]; | 1574 $calls[$id] += $step; |
1201 } | 1575 } |
1202 } | 1576 } |
1203 } | 1577 } |
1204 | 1578 |
1205 /** | 1579 private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null) |
1206 * Returns the inline definition. | 1580 { |
1207 * | 1581 if (null === $definitions) { |
1208 * @param Definition $definition | 1582 $definitions = new \SplObjectStorage(); |
1209 * | 1583 } |
1210 * @return array | 1584 |
1211 */ | |
1212 private function getInlinedDefinitions(Definition $definition) | |
1213 { | |
1214 if (false === $this->inlinedDefinitions->contains($definition)) { | |
1215 $definitions = array_merge( | |
1216 $this->getDefinitionsFromArguments($definition->getArguments()), | |
1217 $this->getDefinitionsFromArguments($definition->getMethodCalls()), | |
1218 $this->getDefinitionsFromArguments($definition->getProperties()), | |
1219 $this->getDefinitionsFromArguments(array($definition->getConfigurator())), | |
1220 $this->getDefinitionsFromArguments(array($definition->getFactory())) | |
1221 ); | |
1222 | |
1223 $this->inlinedDefinitions->offsetSet($definition, $definitions); | |
1224 | |
1225 return $definitions; | |
1226 } | |
1227 | |
1228 return $this->inlinedDefinitions->offsetGet($definition); | |
1229 } | |
1230 | |
1231 /** | |
1232 * Gets the definition from arguments. | |
1233 * | |
1234 * @param array $arguments | |
1235 * | |
1236 * @return array | |
1237 */ | |
1238 private function getDefinitionsFromArguments(array $arguments) | |
1239 { | |
1240 $definitions = array(); | |
1241 foreach ($arguments as $argument) { | 1585 foreach ($arguments as $argument) { |
1242 if (is_array($argument)) { | 1586 if (is_array($argument)) { |
1243 $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument)); | 1587 $this->getDefinitionsFromArguments($argument, $definitions); |
1244 } elseif ($argument instanceof Definition) { | 1588 } elseif (!$argument instanceof Definition) { |
1245 $definitions = array_merge( | 1589 // no-op |
1246 $definitions, | 1590 } elseif (isset($definitions[$argument])) { |
1247 $this->getInlinedDefinitions($argument), | 1591 $definitions[$argument] = 1 + $definitions[$argument]; |
1248 array($argument) | 1592 } else { |
1249 ); | 1593 $definitions[$argument] = 1; |
1594 $this->getDefinitionsFromArguments($argument->getArguments(), $definitions); | |
1595 $this->getDefinitionsFromArguments(array($argument->getFactory()), $definitions); | |
1596 $this->getDefinitionsFromArguments($argument->getProperties(), $definitions); | |
1597 $this->getDefinitionsFromArguments($argument->getMethodCalls(), $definitions); | |
1598 $this->getDefinitionsFromArguments(array($argument->getConfigurator()), $definitions); | |
1599 // move current definition last in the list | |
1600 $nbOccurences = $definitions[$argument]; | |
1601 unset($definitions[$argument]); | |
1602 $definitions[$argument] = $nbOccurences; | |
1250 } | 1603 } |
1251 } | 1604 } |
1252 | 1605 |
1253 return $definitions; | 1606 return $definitions; |
1254 } | 1607 } |
1263 * | 1616 * |
1264 * @return bool | 1617 * @return bool |
1265 */ | 1618 */ |
1266 private function hasReference($id, array $arguments, $deep = false, array &$visited = array()) | 1619 private function hasReference($id, array $arguments, $deep = false, array &$visited = array()) |
1267 { | 1620 { |
1621 if (!isset($this->circularReferences[$id])) { | |
1622 return false; | |
1623 } | |
1624 | |
1268 foreach ($arguments as $argument) { | 1625 foreach ($arguments as $argument) { |
1269 if (is_array($argument)) { | 1626 if (is_array($argument)) { |
1270 if ($this->hasReference($id, $argument, $deep, $visited)) { | 1627 if ($this->hasReference($id, $argument, $deep, $visited)) { |
1271 return true; | 1628 return true; |
1272 } | 1629 } |
1630 | |
1631 continue; | |
1273 } elseif ($argument instanceof Reference) { | 1632 } elseif ($argument instanceof Reference) { |
1274 $argumentId = (string) $argument; | 1633 $argumentId = $this->container->normalizeId($argument); |
1275 if ($id === $argumentId) { | 1634 if ($id === $argumentId) { |
1276 return true; | 1635 return true; |
1277 } | 1636 } |
1278 | 1637 |
1279 if ($deep && !isset($visited[$argumentId]) && 'service_container' !== $argumentId) { | 1638 if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$id][$argumentId])) { |
1280 $visited[$argumentId] = true; | 1639 continue; |
1281 | 1640 } |
1282 $service = $this->container->getDefinition($argumentId); | 1641 |
1283 | 1642 $visited[$argumentId] = true; |
1284 // if the proxy manager is enabled, disable searching for references in lazy services, | 1643 |
1285 // as these services will be instantiated lazily and don't have direct related references. | 1644 $service = $this->container->getDefinition($argumentId); |
1286 if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) { | 1645 } elseif ($argument instanceof Definition) { |
1287 continue; | 1646 $service = $argument; |
1288 } | 1647 } else { |
1289 | 1648 continue; |
1290 $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); | 1649 } |
1291 | 1650 |
1292 if ($this->hasReference($id, $arguments, $deep, $visited)) { | 1651 // if the proxy manager is enabled, disable searching for references in lazy services, |
1293 return true; | 1652 // as these services will be instantiated lazily and don't have direct related references. |
1294 } | 1653 if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) { |
1295 } | 1654 continue; |
1655 } | |
1656 | |
1657 if ($this->hasReference($id, array($service->getArguments(), $service->getFactory(), $service->getProperties(), $service->getMethodCalls(), $service->getConfigurator()), $deep, $visited)) { | |
1658 return true; | |
1296 } | 1659 } |
1297 } | 1660 } |
1298 | 1661 |
1299 return false; | 1662 return false; |
1300 } | 1663 } |
1310 * @throws RuntimeException | 1673 * @throws RuntimeException |
1311 */ | 1674 */ |
1312 private function dumpValue($value, $interpolate = true) | 1675 private function dumpValue($value, $interpolate = true) |
1313 { | 1676 { |
1314 if (is_array($value)) { | 1677 if (is_array($value)) { |
1678 if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) { | |
1679 return $this->dumpValue("%$param%"); | |
1680 } | |
1315 $code = array(); | 1681 $code = array(); |
1316 foreach ($value as $k => $v) { | 1682 foreach ($value as $k => $v) { |
1317 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); | 1683 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); |
1318 } | 1684 } |
1319 | 1685 |
1320 return sprintf('array(%s)', implode(', ', $code)); | 1686 return sprintf('array(%s)', implode(', ', $code)); |
1687 } elseif ($value instanceof ArgumentInterface) { | |
1688 $scope = array($this->definitionVariables, $this->referenceVariables, $this->variableCount); | |
1689 $this->definitionVariables = $this->referenceVariables = null; | |
1690 | |
1691 try { | |
1692 if ($value instanceof ServiceClosureArgument) { | |
1693 $value = $value->getValues()[0]; | |
1694 $code = $this->dumpValue($value, $interpolate); | |
1695 | |
1696 if ($value instanceof TypedReference) { | |
1697 $code = sprintf('$f = function (\\%s $v%s) { return $v; }; return $f(%s);', $value->getType(), ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $value->getInvalidBehavior() ? ' = null' : '', $code); | |
1698 } else { | |
1699 $code = sprintf('return %s;', $code); | |
1700 } | |
1701 | |
1702 return sprintf("function () {\n %s\n }", $code); | |
1703 } | |
1704 | |
1705 if ($value instanceof IteratorArgument) { | |
1706 $operands = array(0); | |
1707 $code = array(); | |
1708 $code[] = 'new RewindableGenerator(function () {'; | |
1709 | |
1710 if (!$values = $value->getValues()) { | |
1711 $code[] = ' return new \EmptyIterator();'; | |
1712 } else { | |
1713 $countCode = array(); | |
1714 $countCode[] = 'function () {'; | |
1715 | |
1716 foreach ($values as $k => $v) { | |
1717 ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; | |
1718 $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); | |
1719 foreach (explode("\n", $v) as $v) { | |
1720 if ($v) { | |
1721 $code[] = ' '.$v; | |
1722 } | |
1723 } | |
1724 } | |
1725 | |
1726 $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); | |
1727 $countCode[] = ' }'; | |
1728 } | |
1729 | |
1730 $code[] = sprintf(' }, %s)', count($operands) > 1 ? implode("\n", $countCode) : $operands[0]); | |
1731 | |
1732 return implode("\n", $code); | |
1733 } | |
1734 } finally { | |
1735 list($this->definitionVariables, $this->referenceVariables, $this->variableCount) = $scope; | |
1736 } | |
1321 } elseif ($value instanceof Definition) { | 1737 } elseif ($value instanceof Definition) { |
1322 if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { | 1738 if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { |
1323 return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate); | 1739 return $this->dumpValue($this->definitionVariables[$value], $interpolate); |
1324 } | 1740 } |
1325 if ($value->getMethodCalls()) { | 1741 if ($value->getMethodCalls()) { |
1326 throw new RuntimeException('Cannot dump definitions which have method calls.'); | 1742 throw new RuntimeException('Cannot dump definitions which have method calls.'); |
1327 } | 1743 } |
1328 if ($value->getProperties()) { | 1744 if ($value->getProperties()) { |
1347 if (is_array($factory)) { | 1763 if (is_array($factory)) { |
1348 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { | 1764 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { |
1349 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); | 1765 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); |
1350 } | 1766 } |
1351 | 1767 |
1768 $class = $this->dumpValue($factory[0]); | |
1352 if (is_string($factory[0])) { | 1769 if (is_string($factory[0])) { |
1353 return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments)); | 1770 return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments)); |
1354 } | 1771 } |
1355 | 1772 |
1356 if ($factory[0] instanceof Definition) { | 1773 if ($factory[0] instanceof Definition) { |
1357 return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); | 1774 if (0 === strpos($class, 'new ')) { |
1775 return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments)); | |
1776 } | |
1777 | |
1778 return sprintf("\\call_user_func(array(%s, '%s')%s)", $class, $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); | |
1358 } | 1779 } |
1359 | 1780 |
1360 if ($factory[0] instanceof Reference) { | 1781 if ($factory[0] instanceof Reference) { |
1361 return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments)); | 1782 return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments)); |
1362 } | 1783 } |
1363 } | 1784 } |
1364 | 1785 |
1365 throw new RuntimeException('Cannot dump definition because of invalid factory'); | 1786 throw new RuntimeException('Cannot dump definition because of invalid factory'); |
1366 } | 1787 } |
1372 | 1793 |
1373 return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); | 1794 return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); |
1374 } elseif ($value instanceof Variable) { | 1795 } elseif ($value instanceof Variable) { |
1375 return '$'.$value; | 1796 return '$'.$value; |
1376 } elseif ($value instanceof Reference) { | 1797 } elseif ($value instanceof Reference) { |
1377 if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) { | 1798 $id = $this->container->normalizeId($value); |
1799 if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) { | |
1378 return $this->dumpValue($this->referenceVariables[$id], $interpolate); | 1800 return $this->dumpValue($this->referenceVariables[$id], $interpolate); |
1379 } | 1801 } |
1380 | 1802 |
1381 return $this->getServiceCall((string) $value, $value); | 1803 return $this->getServiceCall($id, $value); |
1382 } elseif ($value instanceof Expression) { | 1804 } elseif ($value instanceof Expression) { |
1383 return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); | 1805 return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); |
1384 } elseif ($value instanceof Parameter) { | 1806 } elseif ($value instanceof Parameter) { |
1385 return $this->dumpParameter($value); | 1807 return $this->dumpParameter($value); |
1386 } elseif (true === $interpolate && is_string($value)) { | 1808 } elseif (true === $interpolate && is_string($value)) { |
1387 if (preg_match('/^%([^%]+)%$/', $value, $match)) { | 1809 if (preg_match('/^%([^%]+)%$/', $value, $match)) { |
1388 // we do this to deal with non string values (Boolean, integer, ...) | 1810 // we do this to deal with non string values (Boolean, integer, ...) |
1389 // the preg_replace_callback converts them to strings | 1811 // the preg_replace_callback converts them to strings |
1390 return $this->dumpParameter(strtolower($match[1])); | 1812 return $this->dumpParameter($match[1]); |
1391 } else { | 1813 } else { |
1392 $replaceParameters = function ($match) { | 1814 $replaceParameters = function ($match) { |
1393 return "'.".$this->dumpParameter(strtolower($match[2])).".'"; | 1815 return "'.".$this->dumpParameter($match[2]).".'"; |
1394 }; | 1816 }; |
1395 | 1817 |
1396 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value))); | 1818 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value))); |
1397 | 1819 |
1398 return $code; | 1820 return $code; |
1434 * | 1856 * |
1435 * @return string | 1857 * @return string |
1436 */ | 1858 */ |
1437 private function dumpParameter($name) | 1859 private function dumpParameter($name) |
1438 { | 1860 { |
1439 if ($this->container->isFrozen() && $this->container->hasParameter($name)) { | 1861 if ($this->container->isCompiled() && $this->container->hasParameter($name)) { |
1440 return $this->dumpValue($this->container->getParameter($name), false); | 1862 $value = $this->container->getParameter($name); |
1441 } | 1863 $dumpedValue = $this->dumpValue($value, false); |
1442 | 1864 |
1443 return sprintf("\$this->getParameter('%s')", strtolower($name)); | 1865 if (!$value || !is_array($value)) { |
1866 return $dumpedValue; | |
1867 } | |
1868 | |
1869 if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) { | |
1870 return sprintf("\$this->parameters['%s']", $name); | |
1871 } | |
1872 } | |
1873 | |
1874 return sprintf("\$this->getParameter('%s')", $name); | |
1444 } | 1875 } |
1445 | 1876 |
1446 /** | 1877 /** |
1447 * Gets a service call. | 1878 * Gets a service call. |
1448 * | 1879 * |
1454 private function getServiceCall($id, Reference $reference = null) | 1885 private function getServiceCall($id, Reference $reference = null) |
1455 { | 1886 { |
1456 while ($this->container->hasAlias($id)) { | 1887 while ($this->container->hasAlias($id)) { |
1457 $id = (string) $this->container->getAlias($id); | 1888 $id = (string) $this->container->getAlias($id); |
1458 } | 1889 } |
1890 $id = $this->container->normalizeId($id); | |
1459 | 1891 |
1460 if ('service_container' === $id) { | 1892 if ('service_container' === $id) { |
1461 return '$this'; | 1893 return '$this'; |
1462 } | 1894 } |
1463 | 1895 |
1464 if ($this->container->hasDefinition($id) && !$this->container->getDefinition($id)->isPublic()) { | 1896 if ($this->container->hasDefinition($id) && ($definition = $this->container->getDefinition($id)) && !$definition->isSynthetic()) { |
1465 // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? \$this->{$this->generateMethodName($id)}())" on PHP>=7.0 | 1897 if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { |
1466 | 1898 $code = 'null'; |
1467 return "\${(\$_ = isset(\$this->services['$id']) ? \$this->services['$id'] : \$this->{$this->generateMethodName($id)}()) && false ?: '_'}"; | 1899 } elseif ($this->isTrivialInstance($definition)) { |
1468 } | 1900 $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2); |
1469 if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { | 1901 if ($definition->isShared()) { |
1470 return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id); | 1902 $code = sprintf('$this->services[\'%s\'] = %s', $id, $code); |
1471 } | 1903 } |
1472 | 1904 } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) { |
1473 return sprintf('$this->get(\'%s\')', $id); | 1905 $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id)); |
1906 } else { | |
1907 $code = sprintf('$this->%s()', $this->generateMethodName($id)); | |
1908 } | |
1909 } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { | |
1910 return 'null'; | |
1911 } elseif (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { | |
1912 $code = sprintf('$this->get(\'%s\', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $id, ContainerInterface::NULL_ON_INVALID_REFERENCE); | |
1913 } else { | |
1914 $code = sprintf('$this->get(\'%s\')', $id); | |
1915 } | |
1916 | |
1917 // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? $code)" on PHP>=7.0 | |
1918 | |
1919 return "\${(\$_ = isset(\$this->services['$id']) ? \$this->services['$id'] : $code) && false ?: '_'}"; | |
1474 } | 1920 } |
1475 | 1921 |
1476 /** | 1922 /** |
1477 * Initializes the method names map to avoid conflicts with the Container methods. | 1923 * Initializes the method names map to avoid conflicts with the Container methods. |
1478 * | 1924 * |
1481 private function initializeMethodNamesMap($class) | 1927 private function initializeMethodNamesMap($class) |
1482 { | 1928 { |
1483 $this->serviceIdToMethodNameMap = array(); | 1929 $this->serviceIdToMethodNameMap = array(); |
1484 $this->usedMethodNames = array(); | 1930 $this->usedMethodNames = array(); |
1485 | 1931 |
1486 try { | 1932 if ($reflectionClass = $this->container->getReflectionClass($class)) { |
1487 $reflectionClass = new \ReflectionClass($class); | |
1488 foreach ($reflectionClass->getMethods() as $method) { | 1933 foreach ($reflectionClass->getMethods() as $method) { |
1489 $this->usedMethodNames[strtolower($method->getName())] = true; | 1934 $this->usedMethodNames[strtolower($method->getName())] = true; |
1490 } | 1935 } |
1491 } catch (\ReflectionException $e) { | |
1492 } | 1936 } |
1493 } | 1937 } |
1494 | 1938 |
1495 /** | 1939 /** |
1496 * Convert a service id to a valid PHP method name. | 1940 * Convert a service id to a valid PHP method name. |
1505 { | 1949 { |
1506 if (isset($this->serviceIdToMethodNameMap[$id])) { | 1950 if (isset($this->serviceIdToMethodNameMap[$id])) { |
1507 return $this->serviceIdToMethodNameMap[$id]; | 1951 return $this->serviceIdToMethodNameMap[$id]; |
1508 } | 1952 } |
1509 | 1953 |
1510 $name = Container::camelize($id); | 1954 $i = strrpos($id, '\\'); |
1955 $name = Container::camelize(false !== $i && isset($id[1 + $i]) ? substr($id, 1 + $i) : $id); | |
1511 $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name); | 1956 $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name); |
1512 $methodName = 'get'.$name.'Service'; | 1957 $methodName = 'get'.$name.'Service'; |
1513 $suffix = 1; | 1958 $suffix = 1; |
1514 | 1959 |
1515 while (isset($this->usedMethodNames[strtolower($methodName)])) { | 1960 while (isset($this->usedMethodNames[strtolower($methodName)])) { |
1586 } | 2031 } |
1587 | 2032 |
1588 return $this->expressionLanguage; | 2033 return $this->expressionLanguage; |
1589 } | 2034 } |
1590 | 2035 |
1591 private function exportTargetDirs() | 2036 private function isHotPath(Definition $definition) |
1592 { | 2037 { |
1593 return null === $this->targetDirRegex ? '' : <<<EOF | 2038 return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated(); |
1594 | |
1595 \$dir = __DIR__; | |
1596 for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) { | |
1597 \$this->targetDirs[\$i] = \$dir = dirname(\$dir); | |
1598 } | |
1599 EOF; | |
1600 } | 2039 } |
1601 | 2040 |
1602 private function export($value) | 2041 private function export($value) |
1603 { | 2042 { |
1604 if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { | 2043 if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { |
1605 $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1])).'.' : ''; | 2044 $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; |
1606 $suffix = $matches[0][1] + strlen($matches[0][0]); | 2045 $suffix = $matches[0][1] + strlen($matches[0][0]); |
1607 $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix)) : ''; | 2046 $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : ''; |
1608 $dirname = '__DIR__'; | 2047 $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__'; |
1609 | 2048 $offset = 1 + $this->targetDirMaxMatches - count($matches); |
1610 if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) { | 2049 |
2050 if ($this->asFiles || 0 < $offset) { | |
1611 $dirname = sprintf('$this->targetDirs[%d]', $offset); | 2051 $dirname = sprintf('$this->targetDirs[%d]', $offset); |
1612 } | 2052 } |
1613 | 2053 |
1614 if ($prefix || $suffix) { | 2054 if ($prefix || $suffix) { |
1615 return sprintf('(%s%s%s)', $prefix, $dirname, $suffix); | 2055 return sprintf('(%s%s%s)', $prefix, $dirname, $suffix); |
1616 } | 2056 } |
1617 | 2057 |
1618 return $dirname; | 2058 return $dirname; |
1619 } | 2059 } |
1620 | 2060 |
1621 return $this->doExport($value); | 2061 return $this->doExport($value, true); |
1622 } | 2062 } |
1623 | 2063 |
1624 private function doExport($value) | 2064 private function doExport($value, $resolveEnv = false) |
1625 { | 2065 { |
1626 $export = var_export($value, true); | 2066 if (is_string($value) && false !== strpos($value, "\n")) { |
1627 | 2067 $cleanParts = explode("\n", $value); |
1628 if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('%s').'")) { | 2068 $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts); |
2069 $export = implode('."\n".', $cleanParts); | |
2070 } else { | |
2071 $export = var_export($value, true); | |
2072 } | |
2073 | |
2074 if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) { | |
1629 $export = $resolvedExport; | 2075 $export = $resolvedExport; |
2076 if (".''" === substr($export, -3)) { | |
2077 $export = substr($export, 0, -3); | |
2078 if ("'" === $export[1]) { | |
2079 $export = substr_replace($export, '', 18, 7); | |
2080 } | |
2081 } | |
1630 if ("'" === $export[1]) { | 2082 if ("'" === $export[1]) { |
1631 $export = substr($export, 3); | 2083 $export = substr($export, 3); |
1632 } | 2084 } |
1633 if (".''" === substr($export, -3)) { | |
1634 $export = substr($export, 0, -3); | |
1635 } | |
1636 } | 2085 } |
1637 | 2086 |
1638 return $export; | 2087 return $export; |
1639 } | 2088 } |
1640 } | 2089 } |