comparison vendor/symfony/dependency-injection/Dumper/PhpDumper.php @ 4:a9cd425dd02b

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:11:55 +0000
parents 5311817fb629
children 12f9dff5fda9
comparison
equal deleted inserted replaced
3:307d7a7fd348 4:a9cd425dd02b
12 namespace Symfony\Component\DependencyInjection\Dumper; 12 namespace Symfony\Component\DependencyInjection\Dumper;
13 13
14 use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; 14 use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
15 use Symfony\Component\DependencyInjection\Argument\IteratorArgument; 15 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
16 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; 16 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
17 use Symfony\Component\DependencyInjection\Variable; 17 use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
18 use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
19 use Symfony\Component\DependencyInjection\Container;
20 use Symfony\Component\DependencyInjection\ContainerBuilder;
21 use Symfony\Component\DependencyInjection\ContainerInterface;
18 use Symfony\Component\DependencyInjection\Definition; 22 use Symfony\Component\DependencyInjection\Definition;
19 use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
20 use Symfony\Component\DependencyInjection\ContainerBuilder;
21 use Symfony\Component\DependencyInjection\Container;
22 use Symfony\Component\DependencyInjection\ContainerInterface;
23 use Symfony\Component\DependencyInjection\Reference;
24 use Symfony\Component\DependencyInjection\TypedReference;
25 use Symfony\Component\DependencyInjection\Parameter;
26 use Symfony\Component\DependencyInjection\Exception\EnvParameterException; 23 use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
27 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; 24 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
28 use Symfony\Component\DependencyInjection\Exception\RuntimeException; 25 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
29 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; 26 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
27 use Symfony\Component\DependencyInjection\ExpressionLanguage;
30 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; 28 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
31 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; 29 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
32 use Symfony\Component\DependencyInjection\ExpressionLanguage; 30 use Symfony\Component\DependencyInjection\Parameter;
31 use Symfony\Component\DependencyInjection\Reference;
32 use Symfony\Component\DependencyInjection\TypedReference;
33 use Symfony\Component\DependencyInjection\Variable;
33 use Symfony\Component\ExpressionLanguage\Expression; 34 use Symfony\Component\ExpressionLanguage\Expression;
34 use Symfony\Component\HttpKernel\Kernel; 35 use Symfony\Component\HttpKernel\Kernel;
35 36
36 /** 37 /**
37 * PhpDumper dumps a service container as a PHP class. 38 * PhpDumper dumps a service container as a PHP class.
52 const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; 53 const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
53 54
54 private $definitionVariables; 55 private $definitionVariables;
55 private $referenceVariables; 56 private $referenceVariables;
56 private $variableCount; 57 private $variableCount;
57 private $reservedVariables = array('instance', 'class'); 58 private $inlinedDefinitions;
59 private $serviceCalls;
60 private $reservedVariables = ['instance', 'class', 'this'];
58 private $expressionLanguage; 61 private $expressionLanguage;
59 private $targetDirRegex; 62 private $targetDirRegex;
60 private $targetDirMaxMatches; 63 private $targetDirMaxMatches;
61 private $docStar; 64 private $docStar;
62 private $serviceIdToMethodNameMap; 65 private $serviceIdToMethodNameMap;
63 private $usedMethodNames; 66 private $usedMethodNames;
64 private $namespace; 67 private $namespace;
65 private $asFiles; 68 private $asFiles;
66 private $hotPathTag; 69 private $hotPathTag;
67 private $inlineRequires; 70 private $inlineRequires;
68 private $inlinedRequires = array(); 71 private $inlinedRequires = [];
69 private $circularReferences = array(); 72 private $circularReferences = [];
70 73
71 /** 74 /**
72 * @var ProxyDumper 75 * @var ProxyDumper
73 */ 76 */
74 private $proxyDumper; 77 private $proxyDumper;
105 * 108 *
106 * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set 109 * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set
107 * 110 *
108 * @throws EnvParameterException When an env var exists but has not been dumped 111 * @throws EnvParameterException When an env var exists but has not been dumped
109 */ 112 */
110 public function dump(array $options = array()) 113 public function dump(array $options = [])
111 { 114 {
112 $this->targetDirRegex = null; 115 $this->targetDirRegex = null;
113 $this->inlinedRequires = array(); 116 $this->inlinedRequires = [];
114 $options = array_merge(array( 117 $options = array_merge([
115 'class' => 'ProjectServiceContainer', 118 'class' => 'ProjectServiceContainer',
116 'base_class' => 'Container', 119 'base_class' => 'Container',
117 'namespace' => '', 120 'namespace' => '',
118 'as_files' => false, 121 'as_files' => false,
119 'debug' => true, 122 'debug' => true,
120 'hot_path_tag' => 'container.hot_path', 123 'hot_path_tag' => 'container.hot_path',
121 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', 124 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
122 'build_time' => time(), 125 'build_time' => time(),
123 ), $options); 126 ], $options);
124 127
125 $this->namespace = $options['namespace']; 128 $this->namespace = $options['namespace'];
126 $this->asFiles = $options['as_files']; 129 $this->asFiles = $options['as_files'];
127 $this->hotPathTag = $options['hot_path_tag']; 130 $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']); 131 $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']);
136 $baseClassWithNamespace = $baseClass; 139 $baseClassWithNamespace = $baseClass;
137 } 140 }
138 141
139 $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); 142 $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
140 143
141 (new AnalyzeServiceReferencesPass())->process($this->container); 144 if ($this->getProxyDumper() instanceof NullDumper) {
142 $this->circularReferences = array(); 145 (new AnalyzeServiceReferencesPass(true, false))->process($this->container);
143 $checkedNodes = array(); 146 try {
147 (new CheckCircularReferencesPass())->process($this->container);
148 } catch (ServiceCircularReferenceException $e) {
149 $path = $e->getPath();
150 end($path);
151 $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge';
152
153 throw new ServiceCircularReferenceException($e->getServiceId(), $path);
154 }
155 }
156
157 (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container);
158 $checkedNodes = [];
159 $this->circularReferences = [];
144 foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { 160 foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
145 $currentPath = array($id => $id); 161 if (!$node->getValue() instanceof Definition) {
146 $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); 162 continue;
163 }
164 if (!isset($checkedNodes[$id])) {
165 $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes);
166 }
147 } 167 }
148 $this->container->getCompiler()->getServiceReferenceGraph()->clear(); 168 $this->container->getCompiler()->getServiceReferenceGraph()->clear();
169 $checkedNodes = [];
149 170
150 $this->docStar = $options['debug'] ? '*' : ''; 171 $this->docStar = $options['debug'] ? '*' : '';
151 172
152 if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) { 173 if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) {
153 // Build a regexp where the first root dirs are mandatory, 174 // Build a regexp where the first root dirs are mandatory,
154 // but every other sub-dir is optional up to the full path in $dir 175 // but every other sub-dir is optional up to the full path in $dir
155 // Mandate at least 2 root dirs and not more that 5 optional dirs. 176 // Mandate at least 2 root dirs and not more that 5 optional dirs.
156 177
157 $dir = explode(DIRECTORY_SEPARATOR, realpath($dir)); 178 $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir));
158 $i = count($dir); 179 $i = \count($dir);
159 180
160 if (3 <= $i) { 181 if (3 <= $i) {
161 $regex = ''; 182 $regex = '';
162 $lastOptionalDir = $i > 8 ? $i - 5 : 3; 183 $lastOptionalDir = $i > 8 ? $i - 5 : 3;
163 $this->targetDirMaxMatches = $i - $lastOptionalDir; 184 $this->targetDirMaxMatches = $i - $lastOptionalDir;
164 185
165 while (--$i >= $lastOptionalDir) { 186 while (--$i >= $lastOptionalDir) {
166 $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); 187 $regex = sprintf('(%s%s)?', preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex);
167 } 188 }
168 189
169 do { 190 do {
170 $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; 191 $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex;
171 } while (0 < --$i); 192 } while (0 < --$i);
172 193
173 $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; 194 $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#';
174 } 195 }
175 } 196 }
188 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; 209 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
189 210
190 // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. 211 // This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
191 212
192 EOF; 213 EOF;
193 $files = array(); 214 $files = [];
194 215
195 if ($ids = array_keys($this->container->getRemovedIds())) { 216 if ($ids = array_keys($this->container->getRemovedIds())) {
196 sort($ids); 217 sort($ids);
197 $c = "<?php\n\nreturn array(\n"; 218 $c = "<?php\n\nreturn [\n";
198 foreach ($ids as $id) { 219 foreach ($ids as $id) {
199 $c .= ' '.$this->doExport($id)." => true,\n"; 220 $c .= ' '.$this->doExport($id)." => true,\n";
200 } 221 }
201 $files['removed-ids.php'] = $c .= ");\n"; 222 $files['removed-ids.php'] = $c .= "];\n";
202 } 223 }
203 224
204 foreach ($this->generateServiceFiles() as $file => $c) { 225 foreach ($this->generateServiceFiles() as $file => $c) {
205 $files[$file] = $fileStart.$c; 226 $files[$file] = $fileStart.$c;
206 } 227 }
207 foreach ($this->generateProxyClasses() as $file => $c) { 228 foreach ($this->generateProxyClasses() as $file => $c) {
208 $files[$file] = "<?php\n".$c; 229 $files[$file] = "<?php\n".$c;
209 } 230 }
210 $files[$options['class'].'.php'] = $code; 231 $files[$options['class'].'.php'] = $code;
211 $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx')); 232 $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx'));
212 $code = array(); 233 $code = [];
213 234
214 foreach ($files as $file => $c) { 235 foreach ($files as $file => $c) {
215 $code["Container{$hash}/{$file}"] = $c; 236 $code["Container{$hash}/{$file}"] = $c;
216 } 237 }
217 array_pop($code); 238 array_pop($code);
235 256
236 if (!\\class_exists({$options['class']}::class, false)) { 257 if (!\\class_exists({$options['class']}::class, false)) {
237 \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); 258 \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false);
238 } 259 }
239 260
240 return new \\Container{$hash}\\{$options['class']}(array( 261 return new \\Container{$hash}\\{$options['class']}([
241 'container.build_hash' => '$hash', 262 'container.build_hash' => '$hash',
242 'container.build_id' => '$id', 263 'container.build_id' => '$id',
243 'container.build_time' => $time, 264 'container.build_time' => $time,
244 ), __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); 265 ], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}');
245 266
246 EOF; 267 EOF;
247 } else { 268 } else {
248 foreach ($this->generateProxyClasses() as $c) { 269 foreach ($this->generateProxyClasses() as $c) {
249 $code .= $c; 270 $code .= $c;
250 } 271 }
251 } 272 }
252 273
253 $this->targetDirRegex = null; 274 $this->targetDirRegex = null;
254 $this->inlinedRequires = array(); 275 $this->inlinedRequires = [];
255 $this->circularReferences = array(); 276 $this->circularReferences = [];
256 277
257 $unusedEnvs = array(); 278 $unusedEnvs = [];
258 foreach ($this->container->getEnvCounters() as $env => $use) { 279 foreach ($this->container->getEnvCounters() as $env => $use) {
259 if (!$use) { 280 if (!$use) {
260 $unusedEnvs[] = $env; 281 $unusedEnvs[] = $env;
261 } 282 }
262 } 283 }
279 } 300 }
280 301
281 return $this->proxyDumper; 302 return $this->proxyDumper;
282 } 303 }
283 304
284 /** 305 private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [])
285 * Generates Service local temp variables. 306 {
286 * 307 $checkedNodes[$sourceId] = true;
287 * @return string 308 $currentPath[$sourceId] = $sourceId;
288 */ 309
289 private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions)
290 {
291 $allCalls = $calls = $behavior = array();
292
293 foreach ($allInlinedDefinitions as $def) {
294 $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator());
295 $this->getServiceCallsFromArguments($arguments, $allCalls, false, $cId, $behavior, $allInlinedDefinitions[$def]);
296 }
297
298 $isPreInstance = isset($inlinedDefinitions[$definition]) && isset($this->circularReferences[$cId]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared();
299 foreach ($inlinedDefinitions as $def) {
300 $this->getServiceCallsFromArguments(array($def->getArguments(), $def->getFactory()), $calls, $isPreInstance, $cId);
301 if ($def !== $definition) {
302 $arguments = array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator());
303 $this->getServiceCallsFromArguments($arguments, $calls, $isPreInstance && !$this->hasReference($cId, $arguments, true), $cId);
304 }
305 }
306 if (!isset($inlinedDefinitions[$definition])) {
307 $arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator());
308 $this->getServiceCallsFromArguments($arguments, $calls, false, $cId);
309 }
310
311 $code = '';
312 foreach ($calls as $id => $callCount) {
313 if ('service_container' === $id || $id === $cId || isset($this->referenceVariables[$id])) {
314 continue;
315 }
316 if ($callCount <= 1 && $allCalls[$id] <= 1) {
317 continue;
318 }
319
320 $name = $this->getNextVariableName();
321 $this->referenceVariables[$id] = new Variable($name);
322
323 $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id] ? new Reference($id, $behavior[$id]) : null;
324 $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($id, $reference));
325 }
326
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
338 $code .= "\n";
339 }
340
341 return $code;
342 }
343
344 private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath)
345 {
346 foreach ($edges as $edge) { 310 foreach ($edges as $edge) {
347 $node = $edge->getDestNode(); 311 $node = $edge->getDestNode();
348 $id = $node->getId(); 312 $id = $node->getId();
349 313
350 if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { 314 if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
351 // no-op 315 // no-op
352 } elseif (isset($currentPath[$id])) { 316 } elseif (isset($currentPath[$id])) {
317 $currentId = $id;
353 foreach (array_reverse($currentPath) as $parentId) { 318 foreach (array_reverse($currentPath) as $parentId) {
354 $this->circularReferences[$parentId][$id] = $id; 319 $this->circularReferences[$parentId][$currentId] = $currentId;
355 $id = $parentId; 320 if ($parentId === $id) {
321 break;
322 }
323 $currentId = $parentId;
356 } 324 }
357 } elseif (!isset($checkedNodes[$id])) { 325 } elseif (!isset($checkedNodes[$id])) {
358 $checkedNodes[$id] = true; 326 $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath);
359 $currentPath[$id] = $id; 327 } elseif (isset($this->circularReferences[$id])) {
360 $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); 328 $this->connectCircularReferences($id, $currentPath);
361 unset($currentPath[$id]); 329 }
362 } 330 }
363 } 331 unset($currentPath[$sourceId]);
332 }
333
334 private function connectCircularReferences($sourceId, &$currentPath, &$subPath = [])
335 {
336 $subPath[$sourceId] = $sourceId;
337 $currentPath[$sourceId] = $sourceId;
338
339 foreach ($this->circularReferences[$sourceId] as $id) {
340 if (isset($currentPath[$id])) {
341 $currentId = $id;
342 foreach (array_reverse($currentPath) as $parentId) {
343 $this->circularReferences[$parentId][$currentId] = $currentId;
344 if ($parentId === $id) {
345 break;
346 }
347 $currentId = $parentId;
348 }
349 } elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
350 $this->connectCircularReferences($id, $currentPath, $subPath);
351 }
352 }
353 unset($currentPath[$sourceId]);
354 unset($subPath[$sourceId]);
364 } 355 }
365 356
366 private function collectLineage($class, array &$lineage) 357 private function collectLineage($class, array &$lineage)
367 { 358 {
368 if (isset($lineage[$class])) { 359 if (isset($lineage[$class])) {
394 $lineage[$class] = substr($exportedFile, 1, -1); 385 $lineage[$class] = substr($exportedFile, 1, -1);
395 } 386 }
396 387
397 private function generateProxyClasses() 388 private function generateProxyClasses()
398 { 389 {
399 $alreadyGenerated = array(); 390 $alreadyGenerated = [];
400 $definitions = $this->container->getDefinitions(); 391 $definitions = $this->container->getDefinitions();
401 $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); 392 $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
402 $proxyDumper = $this->getProxyDumper(); 393 $proxyDumper = $this->getProxyDumper();
403 ksort($definitions); 394 ksort($definitions);
404 foreach ($definitions as $definition) { 395 foreach ($definitions as $definition) {
409 continue; 400 continue;
410 } 401 }
411 $alreadyGenerated[$class] = true; 402 $alreadyGenerated[$class] = true;
412 // register class' reflector for resource tracking 403 // register class' reflector for resource tracking
413 $this->container->getReflectionClass($class); 404 $this->container->getReflectionClass($class);
414 $proxyCode = "\n".$proxyDumper->getProxyCode($definition); 405 if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) {
406 continue;
407 }
415 if ($strip) { 408 if ($strip) {
416 $proxyCode = "<?php\n".$proxyCode; 409 $proxyCode = "<?php\n".$proxyCode;
417 $proxyCode = substr(Kernel::stripComments($proxyCode), 5); 410 $proxyCode = substr(Kernel::stripComments($proxyCode), 5);
418 } 411 }
419 yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $proxyCode; 412 yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $proxyCode;
423 /** 416 /**
424 * Generates the require_once statement for service includes. 417 * Generates the require_once statement for service includes.
425 * 418 *
426 * @return string 419 * @return string
427 */ 420 */
428 private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions) 421 private function addServiceInclude($cId, Definition $definition)
429 { 422 {
430 $code = ''; 423 $code = '';
431 424
432 if ($this->inlineRequires && !$this->isHotPath($definition)) { 425 if ($this->inlineRequires && !$this->isHotPath($definition)) {
433 $lineage = $calls = $behavior = array(); 426 $lineage = [];
434 foreach ($inlinedDefinitions as $def) { 427 foreach ($this->inlinedDefinitions as $def) {
435 if (!$def->isDeprecated() && is_string($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass())) { 428 if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
436 $this->collectLineage($class, $lineage); 429 $this->collectLineage($class, $lineage);
437 } 430 }
438 $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); 431 }
439 $this->getServiceCallsFromArguments($arguments, $calls, false, $cId, $behavior, $inlinedDefinitions[$def]); 432
440 } 433 foreach ($this->serviceCalls as $id => list($callCount, $behavior)) {
441
442 foreach ($calls as $id => $callCount) {
443 if ('service_container' !== $id && $id !== $cId 434 if ('service_container' !== $id && $id !== $cId
444 && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior[$id] 435 && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
445 && $this->container->has($id) 436 && $this->container->has($id)
446 && $this->isTrivialInstance($def = $this->container->findDefinition($id)) 437 && $this->isTrivialInstance($def = $this->container->findDefinition($id))
447 && is_string($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass()) 438 && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())
448 ) { 439 ) {
449 $this->collectLineage($class, $lineage); 440 $this->collectLineage($class, $lineage);
450 } 441 }
451 } 442 }
452 443
453 foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { 444 foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
454 $code .= sprintf(" include_once %s;\n", $file); 445 $code .= sprintf(" include_once %s;\n", $file);
455 } 446 }
456 } 447 }
457 448
458 foreach ($inlinedDefinitions as $def) { 449 foreach ($this->inlinedDefinitions as $def) {
459 if ($file = $def->getFile()) { 450 if ($file = $def->getFile()) {
460 $code .= sprintf(" include_once %s;\n", $this->dumpValue($file)); 451 $code .= sprintf(" include_once %s;\n", $this->dumpValue($file));
461 } 452 }
462 } 453 }
463 454
464 if ('' !== $code) { 455 if ('' !== $code) {
465 $code .= "\n";
466 }
467
468 return $code;
469 }
470
471 /**
472 * Generates the inline definition of a service.
473 *
474 * @return string
475 *
476 * @throws RuntimeException When the factory definition is incomplete
477 * @throws ServiceCircularReferenceException When a circular reference is detected
478 */
479 private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance)
480 {
481 $code = '';
482
483 foreach ($inlinedDefinitions as $def) {
484 if ($definition === $def) {
485 continue;
486 }
487 if ($inlinedDefinitions[$def] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) {
488 continue;
489 }
490 if (isset($this->definitionVariables[$def])) {
491 $name = $this->definitionVariables[$def];
492 } else {
493 $name = $this->getNextVariableName();
494 $this->definitionVariables[$def] = new Variable($name);
495 }
496
497 // a construct like:
498 // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
499 // this is an indication for a wrong implementation, you can circumvent this problem
500 // by setting up your service structure like this:
501 // $b = new ServiceB();
502 // $a = new ServiceA(ServiceB $b);
503 // $b->setServiceA(ServiceA $a);
504 if (isset($inlinedDefinitions[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) {
505 throw new ServiceCircularReferenceException($id, array($id));
506 }
507
508 $code .= $this->addNewInstance($def, '$'.$name, ' = ', $id);
509
510 if (!$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) {
511 $code .= $this->addServiceProperties($def, $name);
512 $code .= $this->addServiceMethodCalls($def, $name);
513 $code .= $this->addServiceConfigurator($def, $name);
514 } else {
515 $isSimpleInstance = false;
516 }
517
518 $code .= "\n"; 456 $code .= "\n";
519 } 457 }
520 458
521 return $code; 459 return $code;
522 } 460 }
555 $return = 'return '; 493 $return = 'return ';
556 } else { 494 } else {
557 $instantiation .= ' = '; 495 $instantiation .= ' = ';
558 } 496 }
559 497
560 $code = $this->addNewInstance($definition, $return, $instantiation, $id); 498 return $this->addNewInstance($definition, $return, $instantiation, $id);
561
562 if (!$isSimpleInstance) {
563 $code .= "\n";
564 }
565
566 return $code;
567 } 499 }
568 500
569 /** 501 /**
570 * Checks if the definition is a trivial instance. 502 * Checks if the definition is a trivial instance.
571 * 503 *
576 private function isTrivialInstance(Definition $definition) 508 private function isTrivialInstance(Definition $definition)
577 { 509 {
578 if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) { 510 if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) {
579 return false; 511 return false;
580 } 512 }
581 if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < count($definition->getArguments())) { 513 if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < \count($definition->getArguments())) {
582 return false; 514 return false;
583 } 515 }
584 516
585 foreach ($definition->getArguments() as $arg) { 517 foreach ($definition->getArguments() as $arg) {
586 if (!$arg || $arg instanceof Parameter) { 518 if (!$arg || $arg instanceof Parameter) {
587 continue; 519 continue;
588 } 520 }
589 if (is_array($arg) && 3 >= count($arg)) { 521 if (\is_array($arg) && 3 >= \count($arg)) {
590 foreach ($arg as $k => $v) { 522 foreach ($arg as $k => $v) {
591 if ($this->dumpValue($k) !== $this->dumpValue($k, false)) { 523 if ($this->dumpValue($k) !== $this->dumpValue($k, false)) {
592 return false; 524 return false;
593 } 525 }
594 if (!$v || $v instanceof Parameter) { 526 if (!$v || $v instanceof Parameter) {
606 } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) { 538 } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) {
607 return false; 539 return false;
608 } 540 }
609 } 541 }
610 542
611 if (false !== strpos($this->dumpLiteralClass($this->dumpValue($definition->getClass())), '$')) {
612 return false;
613 }
614
615 return true; 543 return true;
616 } 544 }
617 545
618 /** 546 /**
619 * Adds method calls to a service definition. 547 * Adds method calls to a service definition.
625 */ 553 */
626 private function addServiceMethodCalls(Definition $definition, $variableName = 'instance') 554 private function addServiceMethodCalls(Definition $definition, $variableName = 'instance')
627 { 555 {
628 $calls = ''; 556 $calls = '';
629 foreach ($definition->getMethodCalls() as $call) { 557 foreach ($definition->getMethodCalls() as $call) {
630 $arguments = array(); 558 $arguments = [];
631 foreach ($call[1] as $value) { 559 foreach ($call[1] as $value) {
632 $arguments[] = $this->dumpValue($value); 560 $arguments[] = $this->dumpValue($value);
633 } 561 }
634 562
635 $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments))); 563 $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
647 575
648 return $code; 576 return $code;
649 } 577 }
650 578
651 /** 579 /**
652 * Generates the inline definition setup.
653 *
654 * @return string
655 *
656 * @throws ServiceCircularReferenceException when the container contains a circular reference
657 */
658 private function addServiceInlinedDefinitionsSetup($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, $isSimpleInstance)
659 {
660 $this->referenceVariables[$id] = new Variable('instance');
661
662 $code = '';
663 foreach ($inlinedDefinitions as $def) {
664 if ($definition === $def || !$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) {
665 continue;
666 }
667
668 // if the instance is simple, the return statement has already been generated
669 // so, the only possible way to get there is because of a circular reference
670 if ($isSimpleInstance) {
671 throw new ServiceCircularReferenceException($id, array($id));
672 }
673
674 $name = (string) $this->definitionVariables[$def];
675 $code .= $this->addServiceProperties($def, $name);
676 $code .= $this->addServiceMethodCalls($def, $name);
677 $code .= $this->addServiceConfigurator($def, $name);
678 }
679
680 if ('' !== $code && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) {
681 $code .= "\n";
682 }
683
684 return $code;
685 }
686
687 /**
688 * Adds configurator definition. 580 * Adds configurator definition.
689 * 581 *
690 * @param Definition $definition 582 * @param Definition $definition
691 * @param string $variableName 583 * @param string $variableName
692 * 584 *
696 { 588 {
697 if (!$callable = $definition->getConfigurator()) { 589 if (!$callable = $definition->getConfigurator()) {
698 return ''; 590 return '';
699 } 591 }
700 592
701 if (is_array($callable)) { 593 if (\is_array($callable)) {
702 if ($callable[0] instanceof Reference 594 if ($callable[0] instanceof Reference
703 || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { 595 || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))
596 ) {
704 return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); 597 return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
705 } 598 }
706 599
707 $class = $this->dumpValue($callable[0]); 600 $class = $this->dumpValue($callable[0]);
708 // If the class is a string we can optimize call_user_func away 601 // If the class is a string we can optimize call_user_func away
712 605
713 if (0 === strpos($class, 'new ')) { 606 if (0 === strpos($class, 'new ')) {
714 return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); 607 return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
715 } 608 }
716 609
717 return sprintf(" \\call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); 610 return sprintf(" \\call_user_func([%s, '%s'], \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
718 } 611 }
719 612
720 return sprintf(" %s(\$%s);\n", $callable, $variableName); 613 return sprintf(" %s(\$%s);\n", $callable, $variableName);
721 } 614 }
722 615
730 * @return string 623 * @return string
731 */ 624 */
732 private function addService($id, Definition $definition, &$file = null) 625 private function addService($id, Definition $definition, &$file = null)
733 { 626 {
734 $this->definitionVariables = new \SplObjectStorage(); 627 $this->definitionVariables = new \SplObjectStorage();
735 $this->referenceVariables = array(); 628 $this->referenceVariables = [];
736 $this->variableCount = 0; 629 $this->variableCount = 0;
737 630 $this->referenceVariables[$id] = new Variable('instance');
738 $return = array(); 631
632 $return = [];
739 633
740 if ($class = $definition->getClass()) { 634 if ($class = $definition->getClass()) {
741 $class = $this->container->resolveEnvPlaceholders($class); 635 $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
742 $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); 636 $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\'));
743 } elseif ($definition->getFactory()) { 637 } elseif ($definition->getFactory()) {
744 $factory = $definition->getFactory(); 638 $factory = $definition->getFactory();
745 if (is_string($factory)) { 639 if (\is_string($factory)) {
746 $return[] = sprintf('@return object An instance returned by %s()', $factory); 640 $return[] = sprintf('@return object An instance returned by %s()', $factory);
747 } elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { 641 } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
748 if (is_string($factory[0]) || $factory[0] instanceof Reference) { 642 $class = $factory[0] instanceof Definition ? $factory[0]->getClass() : (string) $factory[0];
749 $return[] = sprintf('@return object An instance returned by %s::%s()', (string) $factory[0], $factory[1]); 643 $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
750 } elseif ($factory[0] instanceof Definition) { 644 $return[] = sprintf('@return object An instance returned by %s::%s()', $class, $factory[1]);
751 $return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]);
752 }
753 } 645 }
754 } 646 }
755 647
756 if ($definition->isDeprecated()) { 648 if ($definition->isDeprecated()) {
757 if ($return && 0 === strpos($return[count($return) - 1], '@return')) { 649 if ($return && 0 === strpos($return[\count($return) - 1], '@return')) {
758 $return[] = ''; 650 $return[] = '';
759 } 651 }
760 652
761 $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id)); 653 $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id));
762 } 654 }
767 $shared = $definition->isShared() ? ' shared' : ''; 659 $shared = $definition->isShared() ? ' shared' : '';
768 $public = $definition->isPublic() ? 'public' : 'private'; 660 $public = $definition->isPublic() ? 'public' : 'private';
769 $autowired = $definition->isAutowired() ? ' autowired' : ''; 661 $autowired = $definition->isAutowired() ? ' autowired' : '';
770 662
771 if ($definition->isLazy()) { 663 if ($definition->isLazy()) {
664 unset($this->circularReferences[$id]);
772 $lazyInitialization = '$lazyLoad = true'; 665 $lazyInitialization = '$lazyLoad = true';
773 } else { 666 } else {
774 $lazyInitialization = ''; 667 $lazyInitialization = '';
775 } 668 }
776 669
791 { 684 {
792 685
793 EOF; 686 EOF;
794 } 687 }
795 688
689 $this->serviceCalls = [];
690 $this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls);
691
692 $code .= $this->addServiceInclude($id, $definition);
693
796 if ($this->getProxyDumper()->isProxyCandidate($definition)) { 694 if ($this->getProxyDumper()->isProxyCandidate($definition)) {
797 $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)'; 695 $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)';
798 $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName)); 696 $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName));
799 } 697 }
800 698
801 if ($definition->isDeprecated()) { 699 if ($definition->isDeprecated()) {
802 $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); 700 $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
803 } 701 }
804 702
805 $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); 703 $code .= $this->addInlineService($id, $definition);
806 $constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory()));
807 $otherDefinitions = new \SplObjectStorage();
808
809 foreach ($inlinedDefinitions as $def) {
810 if ($def === $definition || isset($constructorDefinitions[$def])) {
811 $constructorDefinitions[$def] = $inlinedDefinitions[$def];
812 } else {
813 $otherDefinitions[$def] = $inlinedDefinitions[$def];
814 }
815 }
816
817 $isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator();
818
819 $code .=
820 $this->addServiceInclude($id, $definition, $inlinedDefinitions).
821 $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $inlinedDefinitions).
822 $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance).
823 $this->addServiceInstance($id, $definition, $isSimpleInstance).
824 $this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $inlinedDefinitions).
825 $this->addServiceInlinedDefinitions($id, $definition, $otherDefinitions, $isSimpleInstance).
826 $this->addServiceInlinedDefinitionsSetup($id, $definition, $inlinedDefinitions, $isSimpleInstance).
827 $this->addServiceProperties($definition).
828 $this->addServiceMethodCalls($definition).
829 $this->addServiceConfigurator($definition).
830 (!$isSimpleInstance ? "\n return \$instance;\n" : '')
831 ;
832 704
833 if ($asFile) { 705 if ($asFile) {
834 $code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code))); 706 $code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
835 } else { 707 } else {
836 $code .= " }\n"; 708 $code .= " }\n";
837 } 709 }
838 710
839 $this->definitionVariables = null; 711 $this->definitionVariables = $this->inlinedDefinitions = null;
840 $this->referenceVariables = null; 712 $this->referenceVariables = $this->serviceCalls = null;
713
714 return $code;
715 }
716
717 private function addInlineVariables($id, Definition $definition, array $arguments, $forConstructor)
718 {
719 $code = '';
720
721 foreach ($arguments as $argument) {
722 if (\is_array($argument)) {
723 $code .= $this->addInlineVariables($id, $definition, $argument, $forConstructor);
724 } elseif ($argument instanceof Reference) {
725 $code .= $this->addInlineReference($id, $definition, $this->container->normalizeId($argument), $forConstructor);
726 } elseif ($argument instanceof Definition) {
727 $code .= $this->addInlineService($id, $definition, $argument, $forConstructor);
728 }
729 }
730
731 return $code;
732 }
733
734 private function addInlineReference($id, Definition $definition, $targetId, $forConstructor)
735 {
736 list($callCount, $behavior) = $this->serviceCalls[$targetId];
737
738 while ($this->container->hasAlias($targetId)) {
739 $targetId = (string) $this->container->getAlias($targetId);
740 }
741
742 if ($id === $targetId) {
743 return $this->addInlineService($id, $definition, $definition);
744 }
745
746 if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
747 return '';
748 }
749
750 $hasSelfRef = isset($this->circularReferences[$id][$targetId]);
751 $forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
752 $code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
753
754 if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
755 return $code;
756 }
757
758 $name = $this->getNextVariableName();
759 $this->referenceVariables[$targetId] = new Variable($name);
760
761 $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null;
762 $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference));
763
764 if (!$hasSelfRef || !$forConstructor) {
765 return $code;
766 }
767
768 $code .= sprintf(<<<'EOTXT'
769
770 if (isset($this->%s['%s'])) {
771 return $this->%1$s['%2$s'];
772 }
773
774 EOTXT
775 ,
776 'services',
777 $id
778 );
779
780 return $code;
781 }
782
783 private function addInlineService($id, Definition $definition, Definition $inlineDef = null, $forConstructor = true)
784 {
785 $isSimpleInstance = $isRootInstance = null === $inlineDef;
786
787 if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) {
788 return '';
789 }
790
791 $arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()];
792
793 $code = $this->addInlineVariables($id, $definition, $arguments, $forConstructor);
794
795 if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) {
796 $isSimpleInstance = false;
797 } elseif ($definition !== $inlineDef && 2 > $this->inlinedDefinitions[$inlineDef]) {
798 return $code;
799 }
800
801 if (isset($this->definitionVariables[$inlineDef])) {
802 $isSimpleInstance = false;
803 } else {
804 $name = $definition === $inlineDef ? 'instance' : $this->getNextVariableName();
805 $this->definitionVariables[$inlineDef] = new Variable($name);
806 $code .= '' !== $code ? "\n" : '';
807
808 if ('instance' === $name) {
809 $code .= $this->addServiceInstance($id, $definition, $isSimpleInstance);
810 } else {
811 $code .= $this->addNewInstance($inlineDef, '$'.$name, ' = ', $id);
812 }
813
814 if ('' !== $inline = $this->addInlineVariables($id, $definition, $arguments, false)) {
815 $code .= "\n".$inline."\n";
816 } elseif ($arguments && 'instance' === $name) {
817 $code .= "\n";
818 }
819
820 $code .= $this->addServiceProperties($inlineDef, $name);
821 $code .= $this->addServiceMethodCalls($inlineDef, $name);
822 $code .= $this->addServiceConfigurator($inlineDef, $name);
823 }
824
825 if ($isRootInstance && !$isSimpleInstance) {
826 $code .= "\n return \$instance;\n";
827 }
841 828
842 return $code; 829 return $code;
843 } 830 }
844 831
845 /** 832 /**
881 private function addNewInstance(Definition $definition, $return, $instantiation, $id) 868 private function addNewInstance(Definition $definition, $return, $instantiation, $id)
882 { 869 {
883 $class = $this->dumpValue($definition->getClass()); 870 $class = $this->dumpValue($definition->getClass());
884 $return = ' '.$return.$instantiation; 871 $return = ' '.$return.$instantiation;
885 872
886 $arguments = array(); 873 $arguments = [];
887 foreach ($definition->getArguments() as $value) { 874 foreach ($definition->getArguments() as $value) {
888 $arguments[] = $this->dumpValue($value); 875 $arguments[] = $this->dumpValue($value);
889 } 876 }
890 877
891 if (null !== $definition->getFactory()) { 878 if (null !== $definition->getFactory()) {
892 $callable = $definition->getFactory(); 879 $callable = $definition->getFactory();
893 if (is_array($callable)) { 880 if (\is_array($callable)) {
894 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) { 881 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) {
895 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); 882 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a'));
896 } 883 }
897 884
898 if ($callable[0] instanceof Reference 885 if ($callable[0] instanceof Reference
912 899
913 if (0 === strpos($class, 'new ')) { 900 if (0 === strpos($class, 'new ')) {
914 return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); 901 return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : '');
915 } 902 }
916 903
917 return $return.sprintf("\\call_user_func(array(%s, '%s')%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); 904 return $return.sprintf("\\call_user_func([%s, '%s']%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
918 } 905 }
919 906
920 return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); 907 return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '');
921 } 908 }
922 909
959 * @final since Symfony 3.3 946 * @final since Symfony 3.3
960 */ 947 */
961 class $class extends $baseClass 948 class $class extends $baseClass
962 { 949 {
963 private \$parameters; 950 private \$parameters;
964 private \$targetDirs = array(); 951 private \$targetDirs = [];
965 952
966 public function __construct() 953 public function __construct()
967 { 954 {
968 955
969 EOF; 956 EOF;
977 964
978 EOF; 965 EOF;
979 } 966 }
980 if ($this->asFiles) { 967 if ($this->asFiles) {
981 $code = str_replace('$parameters', "\$buildParameters;\n private \$containerDir;\n private \$parameters", $code); 968 $code = str_replace('$parameters', "\$buildParameters;\n private \$containerDir;\n private \$parameters", $code);
982 $code = str_replace('__construct()', '__construct(array $buildParameters = array(), $containerDir = __DIR__)', $code); 969 $code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code);
983 $code .= " \$this->buildParameters = \$buildParameters;\n"; 970 $code .= " \$this->buildParameters = \$buildParameters;\n";
984 $code .= " \$this->containerDir = \$containerDir;\n"; 971 $code .= " \$this->containerDir = \$containerDir;\n";
985 } 972 }
986 973
987 if ($this->container->isCompiled()) { 974 if ($this->container->isCompiled()) {
999 986
1000 if ($this->container->getParameterBag()->all()) { 987 if ($this->container->getParameterBag()->all()) {
1001 $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; 988 $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n";
1002 } 989 }
1003 990
1004 $code .= " \$this->services = array();\n"; 991 $code .= " \$this->services = [];\n";
1005 } else { 992 } else {
1006 $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; 993 $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
1007 $code .= " parent::__construct($arguments);\n"; 994 $code .= " parent::__construct($arguments);\n";
1008 } 995 }
1009 996
1097 if ($this->container->has($normalizedId)) { 1084 if ($this->container->has($normalizedId)) {
1098 $code .= ' '.$this->doExport($id).' => '.$this->doExport($normalizedId).",\n"; 1085 $code .= ' '.$this->doExport($id).' => '.$this->doExport($normalizedId).",\n";
1099 } 1086 }
1100 } 1087 }
1101 1088
1102 return $code ? " \$this->normalizedIds = array(\n".$code." );\n" : ''; 1089 return $code ? " \$this->normalizedIds = [\n".$code." ];\n" : '';
1103 } 1090 }
1104 1091
1105 /** 1092 /**
1106 * Adds the syntheticIds definition. 1093 * Adds the syntheticIds definition.
1107 * 1094 *
1116 if ($definition->isSynthetic() && 'service_container' !== $id) { 1103 if ($definition->isSynthetic() && 'service_container' !== $id) {
1117 $code .= ' '.$this->doExport($id)." => true,\n"; 1104 $code .= ' '.$this->doExport($id)." => true,\n";
1118 } 1105 }
1119 } 1106 }
1120 1107
1121 return $code ? " \$this->syntheticIds = array(\n{$code} );\n" : ''; 1108 return $code ? " \$this->syntheticIds = [\n{$code} ];\n" : '';
1122 } 1109 }
1123 1110
1124 /** 1111 /**
1125 * Adds the removedIds definition. 1112 * Adds the removedIds definition.
1126 * 1113 *
1136 } else { 1123 } else {
1137 $code = ''; 1124 $code = '';
1138 $ids = array_keys($ids); 1125 $ids = array_keys($ids);
1139 sort($ids); 1126 sort($ids);
1140 foreach ($ids as $id) { 1127 foreach ($ids as $id) {
1128 if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id)) {
1129 continue;
1130 }
1141 $code .= ' '.$this->doExport($id)." => true,\n"; 1131 $code .= ' '.$this->doExport($id)." => true,\n";
1142 } 1132 }
1143 1133
1144 $code = "array(\n{$code} )"; 1134 $code = "[\n{$code} ]";
1145 } 1135 }
1146 1136
1147 return <<<EOF 1137 return <<<EOF
1148 1138
1149 public function getRemovedIds() 1139 public function getRemovedIds()
1168 if (!$definition->isSynthetic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) { 1158 if (!$definition->isSynthetic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) {
1169 $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; 1159 $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n";
1170 } 1160 }
1171 } 1161 }
1172 1162
1173 return $code ? " \$this->methodMap = array(\n{$code} );\n" : ''; 1163 return $code ? " \$this->methodMap = [\n{$code} ];\n" : '';
1174 } 1164 }
1175 1165
1176 /** 1166 /**
1177 * Adds the fileMap property definition. 1167 * Adds the fileMap property definition.
1178 * 1168 *
1187 if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) { 1177 if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) {
1188 $code .= sprintf(" %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id)); 1178 $code .= sprintf(" %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id));
1189 } 1179 }
1190 } 1180 }
1191 1181
1192 return $code ? " \$this->fileMap = array(\n{$code} );\n" : ''; 1182 return $code ? " \$this->fileMap = [\n{$code} ];\n" : '';
1193 } 1183 }
1194 1184
1195 /** 1185 /**
1196 * Adds the privates property definition. 1186 * Adds the privates property definition.
1197 * 1187 *
1219 1209
1220 if (empty($code)) { 1210 if (empty($code)) {
1221 return ''; 1211 return '';
1222 } 1212 }
1223 1213
1224 $out = " \$this->privates = array(\n"; 1214 $out = " \$this->privates = [\n";
1225 $out .= $code; 1215 $out .= $code;
1226 $out .= " );\n"; 1216 $out .= " ];\n";
1227 1217
1228 return $out; 1218 return $out;
1229 } 1219 }
1230 1220
1231 /** 1221 /**
1234 * @return string 1224 * @return string
1235 */ 1225 */
1236 private function addAliases() 1226 private function addAliases()
1237 { 1227 {
1238 if (!$aliases = $this->container->getAliases()) { 1228 if (!$aliases = $this->container->getAliases()) {
1239 return $this->container->isCompiled() ? "\n \$this->aliases = array();\n" : ''; 1229 return $this->container->isCompiled() ? "\n \$this->aliases = [];\n" : '';
1240 } 1230 }
1241 1231
1242 $code = " \$this->aliases = array(\n"; 1232 $code = " \$this->aliases = [\n";
1243 ksort($aliases); 1233 ksort($aliases);
1244 foreach ($aliases as $alias => $id) { 1234 foreach ($aliases as $alias => $id) {
1245 $id = $this->container->normalizeId($id); 1235 $id = $this->container->normalizeId($id);
1246 while (isset($aliases[$id])) { 1236 while (isset($aliases[$id])) {
1247 $id = $this->container->normalizeId($aliases[$id]); 1237 $id = $this->container->normalizeId($aliases[$id]);
1248 } 1238 }
1249 $code .= ' '.$this->doExport($alias).' => '.$this->doExport($id).",\n"; 1239 $code .= ' '.$this->doExport($alias).' => '.$this->doExport($id).",\n";
1250 } 1240 }
1251 1241
1252 return $code." );\n"; 1242 return $code." ];\n";
1253 } 1243 }
1254 1244
1255 private function addInlineRequires() 1245 private function addInlineRequires()
1256 { 1246 {
1257 if (!$this->hotPathTag || !$this->inlineRequires) { 1247 if (!$this->hotPathTag || !$this->inlineRequires) {
1258 return ''; 1248 return '';
1259 } 1249 }
1260 1250
1261 $lineage = array(); 1251 $lineage = [];
1262 1252
1263 foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) { 1253 foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) {
1264 $definition = $this->container->getDefinition($id); 1254 $definition = $this->container->getDefinition($id);
1265 $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); 1255 $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);
1266 1256
1267 foreach ($inlinedDefinitions as $def) { 1257 foreach ($inlinedDefinitions as $def) {
1268 if (is_string($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass())) { 1258 if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
1269 $this->collectLineage($class, $lineage); 1259 $this->collectLineage($class, $lineage);
1270 } 1260 }
1271 } 1261 }
1272 } 1262 }
1273 1263
1292 { 1282 {
1293 if (!$this->container->getParameterBag()->all()) { 1283 if (!$this->container->getParameterBag()->all()) {
1294 return ''; 1284 return '';
1295 } 1285 }
1296 1286
1297 $php = array(); 1287 $php = [];
1298 $dynamicPhp = array(); 1288 $dynamicPhp = [];
1299 $normalizedParams = array(); 1289 $normalizedParams = [];
1300 1290
1301 foreach ($this->container->getParameterBag()->all() as $key => $value) { 1291 foreach ($this->container->getParameterBag()->all() as $key => $value) {
1302 if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { 1292 if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) {
1303 throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey)); 1293 throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey));
1304 } 1294 }
1305 if ($key !== $lcKey = strtolower($key)) { 1295 if ($key !== $lcKey = strtolower($key)) {
1306 $normalizedParams[] = sprintf(' %s => %s,', $this->export($lcKey), $this->export($key)); 1296 $normalizedParams[] = sprintf(' %s => %s,', $this->export($lcKey), $this->export($key));
1307 } 1297 }
1308 $export = $this->exportParameters(array($value)); 1298 $export = $this->exportParameters([$value]);
1309 $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2); 1299 $export = explode('0 => ', substr(rtrim($export, " ]\n"), 2, -1), 2);
1310 1300
1311 if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) { 1301 if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) {
1312 $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]); 1302 $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]);
1313 } else { 1303 } else {
1314 $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]); 1304 $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
1315 } 1305 }
1316 } 1306 }
1317 $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8)); 1307
1308 $parameters = sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', 8));
1318 1309
1319 $code = ''; 1310 $code = '';
1320 if ($this->container->isCompiled()) { 1311 if ($this->container->isCompiled()) {
1321 $code .= <<<'EOF' 1312 $code .= <<<'EOF'
1322 1313
1376 if (!$this->asFiles) { 1367 if (!$this->asFiles) {
1377 $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code); 1368 $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code);
1378 } 1369 }
1379 1370
1380 if ($dynamicPhp) { 1371 if ($dynamicPhp) {
1381 $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); 1372 $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8);
1382 $getDynamicParameter = <<<'EOF' 1373 $getDynamicParameter = <<<'EOF'
1383 switch ($name) { 1374 switch ($name) {
1384 %s 1375 %s
1385 default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name)); 1376 default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name));
1386 } 1377 }
1388 1379
1389 return $this->dynamicParameters[$name] = $value; 1380 return $this->dynamicParameters[$name] = $value;
1390 EOF; 1381 EOF;
1391 $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); 1382 $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp));
1392 } else { 1383 } else {
1393 $loadedDynamicParameters = 'array()'; 1384 $loadedDynamicParameters = '[]';
1394 $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; 1385 $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));';
1395 } 1386 }
1396 1387
1397 $code .= <<<EOF 1388 $code .= <<<EOF
1398 1389
1399 private \$loadedDynamicParameters = {$loadedDynamicParameters}; 1390 private \$loadedDynamicParameters = {$loadedDynamicParameters};
1400 private \$dynamicParameters = array(); 1391 private \$dynamicParameters = [];
1401 1392
1402 /*{$this->docStar} 1393 /*{$this->docStar}
1403 * Computes a dynamic parameter. 1394 * Computes a dynamic parameter.
1404 * 1395 *
1405 * @param string The name of the dynamic parameter to load 1396 * @param string \$name The name of the dynamic parameter to load
1406 * 1397 *
1407 * @return mixed The value of the dynamic parameter 1398 * @return mixed The value of the dynamic parameter
1408 * 1399 *
1409 * @throws InvalidArgumentException When the dynamic parameter does not exist 1400 * @throws InvalidArgumentException When the dynamic parameter does not exist
1410 */ 1401 */
1414 } 1405 }
1415 1406
1416 1407
1417 EOF; 1408 EOF;
1418 1409
1419 $code .= ' private $normalizedParameterNames = '.($normalizedParams ? sprintf("array(\n%s\n );", implode("\n", $normalizedParams)) : 'array();')."\n"; 1410 $code .= ' private $normalizedParameterNames = '.($normalizedParams ? sprintf("[\n%s\n ];", implode("\n", $normalizedParams)) : '[];')."\n";
1420 $code .= <<<'EOF' 1411 $code .= <<<'EOF'
1421 1412
1422 private function normalizeParameterName($name) 1413 private function normalizeParameterName($name)
1423 { 1414 {
1424 if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { 1415 if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) {
1466 * 1457 *
1467 * @throws InvalidArgumentException 1458 * @throws InvalidArgumentException
1468 */ 1459 */
1469 private function exportParameters(array $parameters, $path = '', $indent = 12) 1460 private function exportParameters(array $parameters, $path = '', $indent = 12)
1470 { 1461 {
1471 $php = array(); 1462 $php = [];
1472 foreach ($parameters as $key => $value) { 1463 foreach ($parameters as $key => $value) {
1473 if (is_array($value)) { 1464 if (\is_array($value)) {
1474 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); 1465 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
1475 } elseif ($value instanceof ArgumentInterface) { 1466 } elseif ($value instanceof ArgumentInterface) {
1476 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', get_class($value), $path.'/'.$key)); 1467 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', \get_class($value), $path.'/'.$key));
1477 } elseif ($value instanceof Variable) { 1468 } elseif ($value instanceof Variable) {
1478 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); 1469 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
1479 } elseif ($value instanceof Definition) { 1470 } elseif ($value instanceof Definition) {
1480 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)); 1471 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));
1481 } elseif ($value instanceof Reference) { 1472 } elseif ($value instanceof Reference) {
1487 } 1478 }
1488 1479
1489 $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value); 1480 $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value);
1490 } 1481 }
1491 1482
1492 return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4)); 1483 return sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', $indent - 4));
1493 } 1484 }
1494 1485
1495 /** 1486 /**
1496 * Ends the class definition. 1487 * Ends the class definition.
1497 * 1488 *
1528 /** 1519 /**
1529 * Get the conditions to execute for conditional services. 1520 * Get the conditions to execute for conditional services.
1530 * 1521 *
1531 * @param string $value 1522 * @param string $value
1532 * 1523 *
1533 * @return null|string 1524 * @return string|null
1534 */ 1525 */
1535 private function getServiceConditionals($value) 1526 private function getServiceConditionals($value)
1536 { 1527 {
1537 $conditions = array(); 1528 $conditions = [];
1538 foreach (ContainerBuilder::getInitializedConditionals($value) as $service) { 1529 foreach (ContainerBuilder::getInitializedConditionals($value) as $service) {
1539 if (!$this->container->hasDefinition($service)) { 1530 if (!$this->container->hasDefinition($service)) {
1540 return 'false'; 1531 return 'false';
1541 } 1532 }
1542 $conditions[] = sprintf("isset(\$this->services['%s'])", $service); 1533 $conditions[] = sprintf("isset(\$this->services['%s'])", $service);
1554 } 1545 }
1555 1546
1556 return implode(' && ', $conditions); 1547 return implode(' && ', $conditions);
1557 } 1548 }
1558 1549
1559 /** 1550 private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [])
1560 * Builds service calls from arguments. 1551 {
1561 */ 1552 if (null === $definitions) {
1562 private function getServiceCallsFromArguments(array $arguments, array &$calls, $isPreInstance, $callerId, array &$behavior = array(), $step = 1) 1553 $definitions = new \SplObjectStorage();
1563 { 1554 }
1555
1564 foreach ($arguments as $argument) { 1556 foreach ($arguments as $argument) {
1565 if (is_array($argument)) { 1557 if (\is_array($argument)) {
1566 $this->getServiceCallsFromArguments($argument, $calls, $isPreInstance, $callerId, $behavior, $step); 1558 $this->getDefinitionsFromArguments($argument, $definitions, $calls);
1567 } elseif ($argument instanceof Reference) { 1559 } elseif ($argument instanceof Reference) {
1568 $id = $this->container->normalizeId($argument); 1560 $id = $this->container->normalizeId($argument);
1569 1561
1570 if (!isset($calls[$id])) { 1562 if (!isset($calls[$id])) {
1571 $calls[$id] = (int) ($isPreInstance && isset($this->circularReferences[$callerId][$id])); 1563 $calls[$id] = [0, $argument->getInvalidBehavior()];
1572 }
1573 if (!isset($behavior[$id])) {
1574 $behavior[$id] = $argument->getInvalidBehavior();
1575 } else { 1564 } else {
1576 $behavior[$id] = min($behavior[$id], $argument->getInvalidBehavior()); 1565 $calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
1577 } 1566 }
1578 1567
1579 $calls[$id] += $step; 1568 ++$calls[$id][0];
1580 }
1581 }
1582 }
1583
1584 private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null)
1585 {
1586 if (null === $definitions) {
1587 $definitions = new \SplObjectStorage();
1588 }
1589
1590 foreach ($arguments as $argument) {
1591 if (is_array($argument)) {
1592 $this->getDefinitionsFromArguments($argument, $definitions);
1593 } elseif (!$argument instanceof Definition) { 1569 } elseif (!$argument instanceof Definition) {
1594 // no-op 1570 // no-op
1595 } elseif (isset($definitions[$argument])) { 1571 } elseif (isset($definitions[$argument])) {
1596 $definitions[$argument] = 1 + $definitions[$argument]; 1572 $definitions[$argument] = 1 + $definitions[$argument];
1597 } else { 1573 } else {
1598 $definitions[$argument] = 1; 1574 $definitions[$argument] = 1;
1599 $this->getDefinitionsFromArguments($argument->getArguments(), $definitions); 1575 $arguments = [$argument->getArguments(), $argument->getFactory(), $argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()];
1600 $this->getDefinitionsFromArguments(array($argument->getFactory()), $definitions); 1576 $this->getDefinitionsFromArguments($arguments, $definitions, $calls);
1601 $this->getDefinitionsFromArguments($argument->getProperties(), $definitions);
1602 $this->getDefinitionsFromArguments($argument->getMethodCalls(), $definitions);
1603 $this->getDefinitionsFromArguments(array($argument->getConfigurator()), $definitions);
1604 // move current definition last in the list
1605 $nbOccurences = $definitions[$argument];
1606 unset($definitions[$argument]);
1607 $definitions[$argument] = $nbOccurences;
1608 } 1577 }
1609 } 1578 }
1610 1579
1611 return $definitions; 1580 return $definitions;
1612 }
1613
1614 /**
1615 * Checks if a service id has a reference.
1616 *
1617 * @param string $id
1618 * @param array $arguments
1619 * @param bool $deep
1620 * @param array $visited
1621 *
1622 * @return bool
1623 */
1624 private function hasReference($id, array $arguments, $deep = false, array &$visited = array())
1625 {
1626 if (!isset($this->circularReferences[$id])) {
1627 return false;
1628 }
1629
1630 foreach ($arguments as $argument) {
1631 if (is_array($argument)) {
1632 if ($this->hasReference($id, $argument, $deep, $visited)) {
1633 return true;
1634 }
1635
1636 continue;
1637 } elseif ($argument instanceof Reference) {
1638 $argumentId = $this->container->normalizeId($argument);
1639 if ($id === $argumentId) {
1640 return true;
1641 }
1642
1643 if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$id][$argumentId])) {
1644 continue;
1645 }
1646
1647 $visited[$argumentId] = true;
1648
1649 $service = $this->container->getDefinition($argumentId);
1650 } elseif ($argument instanceof Definition) {
1651 $service = $argument;
1652 } else {
1653 continue;
1654 }
1655
1656 // if the proxy manager is enabled, disable searching for references in lazy services,
1657 // as these services will be instantiated lazily and don't have direct related references.
1658 if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) {
1659 continue;
1660 }
1661
1662 if ($this->hasReference($id, array($service->getArguments(), $service->getFactory(), $service->getProperties(), $service->getMethodCalls(), $service->getConfigurator()), $deep, $visited)) {
1663 return true;
1664 }
1665 }
1666
1667 return false;
1668 } 1581 }
1669 1582
1670 /** 1583 /**
1671 * Dumps values. 1584 * Dumps values.
1672 * 1585 *
1677 * 1590 *
1678 * @throws RuntimeException 1591 * @throws RuntimeException
1679 */ 1592 */
1680 private function dumpValue($value, $interpolate = true) 1593 private function dumpValue($value, $interpolate = true)
1681 { 1594 {
1682 if (is_array($value)) { 1595 if (\is_array($value)) {
1683 if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) { 1596 if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) {
1684 return $this->dumpValue("%$param%"); 1597 return $this->dumpValue("%$param%");
1685 } 1598 }
1686 $code = array(); 1599 $code = [];
1687 foreach ($value as $k => $v) { 1600 foreach ($value as $k => $v) {
1688 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); 1601 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1689 } 1602 }
1690 1603
1691 return sprintf('array(%s)', implode(', ', $code)); 1604 return sprintf('[%s]', implode(', ', $code));
1692 } elseif ($value instanceof ArgumentInterface) { 1605 } elseif ($value instanceof ArgumentInterface) {
1693 $scope = array($this->definitionVariables, $this->referenceVariables, $this->variableCount); 1606 $scope = [$this->definitionVariables, $this->referenceVariables];
1694 $this->definitionVariables = $this->referenceVariables = null; 1607 $this->definitionVariables = $this->referenceVariables = null;
1695 1608
1696 try { 1609 try {
1697 if ($value instanceof ServiceClosureArgument) { 1610 if ($value instanceof ServiceClosureArgument) {
1698 $value = $value->getValues()[0]; 1611 $value = $value->getValues()[0];
1706 1619
1707 return sprintf("function () {\n %s\n }", $code); 1620 return sprintf("function () {\n %s\n }", $code);
1708 } 1621 }
1709 1622
1710 if ($value instanceof IteratorArgument) { 1623 if ($value instanceof IteratorArgument) {
1711 $operands = array(0); 1624 $operands = [0];
1712 $code = array(); 1625 $code = [];
1713 $code[] = 'new RewindableGenerator(function () {'; 1626 $code[] = 'new RewindableGenerator(function () {';
1714 1627
1715 if (!$values = $value->getValues()) { 1628 if (!$values = $value->getValues()) {
1716 $code[] = ' return new \EmptyIterator();'; 1629 $code[] = ' return new \EmptyIterator();';
1717 } else { 1630 } else {
1718 $countCode = array(); 1631 $countCode = [];
1719 $countCode[] = 'function () {'; 1632 $countCode[] = 'function () {';
1720 1633
1721 foreach ($values as $k => $v) { 1634 foreach ($values as $k => $v) {
1722 ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; 1635 ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0];
1723 $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); 1636 $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)));
1730 1643
1731 $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); 1644 $countCode[] = sprintf(' return %s;', implode(' + ', $operands));
1732 $countCode[] = ' }'; 1645 $countCode[] = ' }';
1733 } 1646 }
1734 1647
1735 $code[] = sprintf(' }, %s)', count($operands) > 1 ? implode("\n", $countCode) : $operands[0]); 1648 $code[] = sprintf(' }, %s)', \count($operands) > 1 ? implode("\n", $countCode) : $operands[0]);
1736 1649
1737 return implode("\n", $code); 1650 return implode("\n", $code);
1738 } 1651 }
1739 } finally { 1652 } finally {
1740 list($this->definitionVariables, $this->referenceVariables, $this->variableCount) = $scope; 1653 list($this->definitionVariables, $this->referenceVariables) = $scope;
1741 } 1654 }
1742 } elseif ($value instanceof Definition) { 1655 } elseif ($value instanceof Definition) {
1743 if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { 1656 if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
1744 return $this->dumpValue($this->definitionVariables[$value], $interpolate); 1657 return $this->dumpValue($this->definitionVariables[$value], $interpolate);
1745 } 1658 }
1751 } 1664 }
1752 if (null !== $value->getConfigurator()) { 1665 if (null !== $value->getConfigurator()) {
1753 throw new RuntimeException('Cannot dump definitions which have a configurator.'); 1666 throw new RuntimeException('Cannot dump definitions which have a configurator.');
1754 } 1667 }
1755 1668
1756 $arguments = array(); 1669 $arguments = [];
1757 foreach ($value->getArguments() as $argument) { 1670 foreach ($value->getArguments() as $argument) {
1758 $arguments[] = $this->dumpValue($argument); 1671 $arguments[] = $this->dumpValue($argument);
1759 } 1672 }
1760 1673
1761 if (null !== $value->getFactory()) { 1674 if (null !== $value->getFactory()) {
1762 $factory = $value->getFactory(); 1675 $factory = $value->getFactory();
1763 1676
1764 if (is_string($factory)) { 1677 if (\is_string($factory)) {
1765 return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments)); 1678 return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments));
1766 } 1679 }
1767 1680
1768 if (is_array($factory)) { 1681 if (\is_array($factory)) {
1769 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { 1682 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) {
1770 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); 1683 throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a'));
1771 } 1684 }
1772 1685
1773 $class = $this->dumpValue($factory[0]); 1686 $class = $this->dumpValue($factory[0]);
1774 if (is_string($factory[0])) { 1687 if (\is_string($factory[0])) {
1775 return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments)); 1688 return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments));
1776 } 1689 }
1777 1690
1778 if ($factory[0] instanceof Definition) { 1691 if ($factory[0] instanceof Definition) {
1779 if (0 === strpos($class, 'new ')) { 1692 if (0 === strpos($class, 'new ')) {
1780 return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments)); 1693 return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments));
1781 } 1694 }
1782 1695
1783 return sprintf("\\call_user_func(array(%s, '%s')%s)", $class, $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); 1696 return sprintf("\\call_user_func([%s, '%s']%s)", $class, $factory[1], \count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
1784 } 1697 }
1785 1698
1786 if ($factory[0] instanceof Reference) { 1699 if ($factory[0] instanceof Reference) {
1787 return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments)); 1700 return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments));
1788 } 1701 }
1805 return $this->dumpValue($this->referenceVariables[$id], $interpolate); 1718 return $this->dumpValue($this->referenceVariables[$id], $interpolate);
1806 } 1719 }
1807 1720
1808 return $this->getServiceCall($id, $value); 1721 return $this->getServiceCall($id, $value);
1809 } elseif ($value instanceof Expression) { 1722 } elseif ($value instanceof Expression) {
1810 return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); 1723 return $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']);
1811 } elseif ($value instanceof Parameter) { 1724 } elseif ($value instanceof Parameter) {
1812 return $this->dumpParameter($value); 1725 return $this->dumpParameter($value);
1813 } elseif (true === $interpolate && is_string($value)) { 1726 } elseif (true === $interpolate && \is_string($value)) {
1814 if (preg_match('/^%([^%]+)%$/', $value, $match)) { 1727 if (preg_match('/^%([^%]+)%$/', $value, $match)) {
1815 // we do this to deal with non string values (Boolean, integer, ...) 1728 // we do this to deal with non string values (Boolean, integer, ...)
1816 // the preg_replace_callback converts them to strings 1729 // the preg_replace_callback converts them to strings
1817 return $this->dumpParameter($match[1]); 1730 return $this->dumpParameter($match[1]);
1818 } else { 1731 } else {
1822 1735
1823 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value))); 1736 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
1824 1737
1825 return $code; 1738 return $code;
1826 } 1739 }
1827 } elseif (is_object($value) || is_resource($value)) { 1740 } elseif (\is_object($value) || \is_resource($value)) {
1828 throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); 1741 throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
1829 } 1742 }
1830 1743
1831 return $this->export($value); 1744 return $this->export($value);
1832 } 1745 }
1865 { 1778 {
1866 if ($this->container->isCompiled() && $this->container->hasParameter($name)) { 1779 if ($this->container->isCompiled() && $this->container->hasParameter($name)) {
1867 $value = $this->container->getParameter($name); 1780 $value = $this->container->getParameter($name);
1868 $dumpedValue = $this->dumpValue($value, false); 1781 $dumpedValue = $this->dumpValue($value, false);
1869 1782
1870 if (!$value || !is_array($value)) { 1783 if (!$value || !\is_array($value)) {
1871 return $dumpedValue; 1784 return $dumpedValue;
1872 } 1785 }
1873 1786
1874 if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) { 1787 if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) {
1875 return sprintf("\$this->parameters['%s']", $name); 1788 return sprintf("\$this->parameters['%s']", $name);
1896 1809
1897 if ('service_container' === $id) { 1810 if ('service_container' === $id) {
1898 return '$this'; 1811 return '$this';
1899 } 1812 }
1900 1813
1901 if ($this->container->hasDefinition($id) && ($definition = $this->container->getDefinition($id)) && !$definition->isSynthetic()) { 1814 if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) {
1902 if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { 1815 if ($definition->isSynthetic()) {
1816 $code = sprintf('$this->get(\'%s\'%s)', $id, null !== $reference ? ', '.$reference->getInvalidBehavior() : '');
1817 } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
1903 $code = 'null'; 1818 $code = 'null';
1904 if (!$definition->isShared()) { 1819 if (!$definition->isShared()) {
1905 return $code; 1820 return $code;
1906 } 1821 }
1907 } elseif ($this->isTrivialInstance($definition)) { 1822 } elseif ($this->isTrivialInstance($definition)) {
1908 $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2); 1823 $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2);
1909 if ($definition->isShared()) { 1824 if ($definition->isShared()) {
1910 $code = sprintf('$this->services[\'%s\'] = %s', $id, $code); 1825 $code = sprintf('$this->services[\'%s\'] = %s', $id, $code);
1911 } 1826 }
1827 $code = "($code)";
1912 } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) { 1828 } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) {
1913 $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id)); 1829 $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id));
1914 } else { 1830 } else {
1915 $code = sprintf('$this->%s()', $this->generateMethodName($id)); 1831 $code = sprintf('$this->%s()', $this->generateMethodName($id));
1916 } 1832 }
1932 * 1848 *
1933 * @param string $class the container base class 1849 * @param string $class the container base class
1934 */ 1850 */
1935 private function initializeMethodNamesMap($class) 1851 private function initializeMethodNamesMap($class)
1936 { 1852 {
1937 $this->serviceIdToMethodNameMap = array(); 1853 $this->serviceIdToMethodNameMap = [];
1938 $this->usedMethodNames = array(); 1854 $this->usedMethodNames = [];
1939 1855
1940 if ($reflectionClass = $this->container->getReflectionClass($class)) { 1856 if ($reflectionClass = $this->container->getReflectionClass($class)) {
1941 foreach ($reflectionClass->getMethods() as $method) { 1857 foreach ($reflectionClass->getMethods() as $method) {
1942 $this->usedMethodNames[strtolower($method->getName())] = true; 1858 $this->usedMethodNames[strtolower($method->getName())] = true;
1943 } 1859 }
1982 * @return string 1898 * @return string
1983 */ 1899 */
1984 private function getNextVariableName() 1900 private function getNextVariableName()
1985 { 1901 {
1986 $firstChars = self::FIRST_CHARS; 1902 $firstChars = self::FIRST_CHARS;
1987 $firstCharsLength = strlen($firstChars); 1903 $firstCharsLength = \strlen($firstChars);
1988 $nonFirstChars = self::NON_FIRST_CHARS; 1904 $nonFirstChars = self::NON_FIRST_CHARS;
1989 $nonFirstCharsLength = strlen($nonFirstChars); 1905 $nonFirstCharsLength = \strlen($nonFirstChars);
1990 1906
1991 while (true) { 1907 while (true) {
1992 $name = ''; 1908 $name = '';
1993 $i = $this->variableCount; 1909 $i = $this->variableCount;
1994 1910
2004 } 1920 }
2005 1921
2006 ++$this->variableCount; 1922 ++$this->variableCount;
2007 1923
2008 // check that the name is not reserved 1924 // check that the name is not reserved
2009 if (in_array($name, $this->reservedVariables, true)) { 1925 if (\in_array($name, $this->reservedVariables, true)) {
2010 continue; 1926 continue;
2011 } 1927 }
2012 1928
2013 return $name; 1929 return $name;
2014 } 1930 }
2046 return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated(); 1962 return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated();
2047 } 1963 }
2048 1964
2049 private function export($value) 1965 private function export($value)
2050 { 1966 {
2051 if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { 1967 if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {
2052 $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; 1968 $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : '';
2053 $suffix = $matches[0][1] + strlen($matches[0][0]); 1969 $suffix = $matches[0][1] + \strlen($matches[0][0]);
2054 $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : ''; 1970 $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : '';
2055 $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__'; 1971 $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__';
2056 $offset = 1 + $this->targetDirMaxMatches - count($matches); 1972 $offset = 1 + $this->targetDirMaxMatches - \count($matches);
2057 1973
2058 if ($this->asFiles || 0 < $offset) { 1974 if ($this->asFiles || 0 < $offset) {
2059 $dirname = sprintf('$this->targetDirs[%d]', $offset); 1975 $dirname = sprintf('$this->targetDirs[%d]', $offset);
2060 } 1976 }
2061 1977
2069 return $this->doExport($value, true); 1985 return $this->doExport($value, true);
2070 } 1986 }
2071 1987
2072 private function doExport($value, $resolveEnv = false) 1988 private function doExport($value, $resolveEnv = false)
2073 { 1989 {
2074 if (is_string($value) && false !== strpos($value, "\n")) { 1990 if (\is_string($value) && false !== strpos($value, "\n")) {
2075 $cleanParts = explode("\n", $value); 1991 $cleanParts = explode("\n", $value);
2076 $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts); 1992 $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts);
2077 $export = implode('."\n".', $cleanParts); 1993 $export = implode('."\n".', $cleanParts);
2078 } else { 1994 } else {
2079 $export = var_export($value, true); 1995 $export = var_export($value, true);