4aee93dec5037677338e35e8d1b6f7c8b78358db
[yaffs-website] / vendor / symfony / dependency-injection / Dumper / PhpDumper.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\DependencyInjection\Dumper;
13
14 use Symfony\Component\DependencyInjection\Variable;
15 use Symfony\Component\DependencyInjection\Definition;
16 use Symfony\Component\DependencyInjection\ContainerBuilder;
17 use Symfony\Component\DependencyInjection\Container;
18 use Symfony\Component\DependencyInjection\ContainerInterface;
19 use Symfony\Component\DependencyInjection\Reference;
20 use Symfony\Component\DependencyInjection\Parameter;
21 use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
22 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
23 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
24 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
25 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
26 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
27 use Symfony\Component\DependencyInjection\ExpressionLanguage;
28 use Symfony\Component\ExpressionLanguage\Expression;
29 use Symfony\Component\HttpKernel\Kernel;
30
31 /**
32  * PhpDumper dumps a service container as a PHP class.
33  *
34  * @author Fabien Potencier <fabien@symfony.com>
35  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
36  */
37 class PhpDumper extends Dumper
38 {
39     /**
40      * Characters that might appear in the generated variable name as first character.
41      *
42      * @var string
43      */
44     const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
45
46     /**
47      * Characters that might appear in the generated variable name as any but the first character.
48      *
49      * @var string
50      */
51     const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
52
53     private $inlinedDefinitions;
54     private $definitionVariables;
55     private $referenceVariables;
56     private $variableCount;
57     private $reservedVariables = array('instance', 'class');
58     private $expressionLanguage;
59     private $targetDirRegex;
60     private $targetDirMaxMatches;
61     private $docStar;
62     private $serviceIdToMethodNameMap;
63     private $usedMethodNames;
64
65     /**
66      * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
67      */
68     private $proxyDumper;
69
70     /**
71      * {@inheritdoc}
72      */
73     public function __construct(ContainerBuilder $container)
74     {
75         parent::__construct($container);
76
77         $this->inlinedDefinitions = new \SplObjectStorage();
78     }
79
80     /**
81      * Sets the dumper to be used when dumping proxies in the generated container.
82      *
83      * @param ProxyDumper $proxyDumper
84      */
85     public function setProxyDumper(ProxyDumper $proxyDumper)
86     {
87         $this->proxyDumper = $proxyDumper;
88     }
89
90     /**
91      * Dumps the service container as a PHP class.
92      *
93      * Available options:
94      *
95      *  * class:      The class name
96      *  * base_class: The base class name
97      *  * namespace:  The class namespace
98      *
99      * @param array $options An array of options
100      *
101      * @return string A PHP class representing of the service container
102      *
103      * @throws EnvParameterException When an env var exists but has not been dumped
104      */
105     public function dump(array $options = array())
106     {
107         $this->targetDirRegex = null;
108         $options = array_merge(array(
109             'class' => 'ProjectServiceContainer',
110             'base_class' => 'Container',
111             'namespace' => '',
112             'debug' => true,
113         ), $options);
114
115         $this->initializeMethodNamesMap($options['base_class']);
116
117         $this->docStar = $options['debug'] ? '*' : '';
118
119         if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
120             // Build a regexp where the first root dirs are mandatory,
121             // but every other sub-dir is optional up to the full path in $dir
122             // Mandate at least 2 root dirs and not more that 5 optional dirs.
123
124             $dir = explode(DIRECTORY_SEPARATOR, realpath($dir));
125             $i = count($dir);
126
127             if (3 <= $i) {
128                 $regex = '';
129                 $lastOptionalDir = $i > 8 ? $i - 5 : 3;
130                 $this->targetDirMaxMatches = $i - $lastOptionalDir;
131
132                 while (--$i >= $lastOptionalDir) {
133                     $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex);
134                 }
135
136                 do {
137                     $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex;
138                 } while (0 < --$i);
139
140                 $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#';
141             }
142         }
143
144         $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']);
145
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().
156             $this->addDefaultParametersMethod().
157             $this->endClass().
158             $this->addProxyClasses()
159         ;
160         $this->targetDirRegex = null;
161
162         $unusedEnvs = array();
163         foreach ($this->container->getEnvCounters() as $env => $use) {
164             if (!$use) {
165                 $unusedEnvs[] = $env;
166             }
167         }
168         if ($unusedEnvs) {
169             throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.');
170         }
171
172         return $code;
173     }
174
175     /**
176      * Retrieves the currently set proxy dumper or instantiates one.
177      *
178      * @return ProxyDumper
179      */
180     private function getProxyDumper()
181     {
182         if (!$this->proxyDumper) {
183             $this->proxyDumper = new NullDumper();
184         }
185
186         return $this->proxyDumper;
187     }
188
189     /**
190      * Generates Service local temp variables.
191      *
192      * @param string $cId
193      * @param string $definition
194      *
195      * @return string
196      */
197     private function addServiceLocalTempVariables($cId, $definition)
198     {
199         static $template = "        \$%s = %s;\n";
200
201         $localDefinitions = array_merge(
202             array($definition),
203             $this->getInlinedDefinitions($definition)
204         );
205
206         $calls = $behavior = array();
207         foreach ($localDefinitions as $iDefinition) {
208             $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
209             $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
210             $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
211             $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior);
212             $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior);
213         }
214
215         $code = '';
216         foreach ($calls as $id => $callCount) {
217             if ('service_container' === $id || $id === $cId) {
218                 continue;
219             }
220
221             if ($callCount > 1) {
222                 $name = $this->getNextVariableName();
223                 $this->referenceVariables[$id] = new Variable($name);
224
225                 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
226                     $code .= sprintf($template, $name, $this->getServiceCall($id));
227                 } else {
228                     $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
229                 }
230             }
231         }
232
233         if ('' !== $code) {
234             $code .= "\n";
235         }
236
237         return $code;
238     }
239
240     /**
241      * Generates code for the proxies to be attached after the container class.
242      *
243      * @return string
244      */
245     private function addProxyClasses()
246     {
247         /* @var $definitions Definition[] */
248         $definitions = array_filter(
249             $this->container->getDefinitions(),
250             array($this->getProxyDumper(), 'isProxyCandidate')
251         );
252         $code = '';
253         $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
254
255         foreach ($definitions as $definition) {
256             $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition);
257             if ($strip) {
258                 $proxyCode = "<?php\n".$proxyCode;
259                 $proxyCode = substr(Kernel::stripComments($proxyCode), 5);
260             }
261             $code .= $proxyCode;
262         }
263
264         return $code;
265     }
266
267     /**
268      * Generates the require_once statement for service includes.
269      *
270      * @param Definition $definition
271      *
272      * @return string
273      */
274     private function addServiceInclude($definition)
275     {
276         $template = "        require_once %s;\n";
277         $code = '';
278
279         if (null !== $file = $definition->getFile()) {
280             $code .= sprintf($template, $this->dumpValue($file));
281         }
282
283         foreach ($this->getInlinedDefinitions($definition) as $definition) {
284             if (null !== $file = $definition->getFile()) {
285                 $code .= sprintf($template, $this->dumpValue($file));
286             }
287         }
288
289         if ('' !== $code) {
290             $code .= "\n";
291         }
292
293         return $code;
294     }
295
296     /**
297      * Generates the inline definition of a service.
298      *
299      * @param string     $id
300      * @param Definition $definition
301      *
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      *
384      * @return string
385      *
386      * @throws InvalidArgumentException
387      * @throws RuntimeException
388      */
389     private function addServiceInstance($id, Definition $definition)
390     {
391         $class = $this->dumpValue($definition->getClass());
392
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)) {
394             throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
395         }
396
397         $simple = $this->isSimpleInstance($id, $definition);
398         $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
399         $instantiation = '';
400
401         if (!$isProxyCandidate && $definition->isShared()) {
402             $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
403         } elseif (!$simple) {
404             $instantiation = '$instance';
405         }
406
407         $return = '';
408         if ($simple) {
409             $return = 'return ';
410         } else {
411             $instantiation .= ' = ';
412         }
413
414         $code = $this->addNewInstance($definition, $return, $instantiation, $id);
415
416         if (!$simple) {
417             $code .= "\n";
418         }
419
420         return $code;
421     }
422
423     /**
424      * Checks if the definition is a simple instance.
425      *
426      * @param string     $id
427      * @param Definition $definition
428      *
429      * @return bool
430      */
431     private function isSimpleInstance($id, Definition $definition)
432     {
433         foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
434             if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
435                 continue;
436             }
437
438             if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
439                 return false;
440             }
441         }
442
443         return true;
444     }
445
446     /**
447      * Adds method calls to a service definition.
448      *
449      * @param Definition $definition
450      * @param string     $variableName
451      *
452      * @return string
453      */
454     private function addServiceMethodCalls(Definition $definition, $variableName = 'instance')
455     {
456         $calls = '';
457         foreach ($definition->getMethodCalls() as $call) {
458             $arguments = array();
459             foreach ($call[1] as $value) {
460                 $arguments[] = $this->dumpValue($value);
461             }
462
463             $calls .= $this->wrapServiceConditionals($call[1], sprintf("        \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
464         }
465
466         return $calls;
467     }
468
469     private function addServiceProperties(Definition $definition, $variableName = 'instance')
470     {
471         $code = '';
472         foreach ($definition->getProperties() as $name => $value) {
473             $code .= sprintf("        \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
474         }
475
476         return $code;
477     }
478
479     /**
480      * Generates the inline definition setup.
481      *
482      * @param string     $id
483      * @param Definition $definition
484      *
485      * @return string
486      *
487      * @throws ServiceCircularReferenceException when the container contains a circular reference
488      */
489     private function addServiceInlinedDefinitionsSetup($id, Definition $definition)
490     {
491         $this->referenceVariables[$id] = new Variable('instance');
492
493         $code = '';
494         $processed = new \SplObjectStorage();
495         foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
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;
503             }
504
505             // 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
507             if ($this->isSimpleInstance($id, $definition)) {
508                 throw new ServiceCircularReferenceException($id, array($id));
509             }
510
511             $name = (string) $this->definitionVariables->offsetGet($iDefinition);
512             $code .= $this->addServiceProperties($iDefinition, $name);
513             $code .= $this->addServiceMethodCalls($iDefinition, $name);
514             $code .= $this->addServiceConfigurator($iDefinition, $name);
515         }
516
517         if ('' !== $code) {
518             $code .= "\n";
519         }
520
521         return $code;
522     }
523
524     /**
525      * Adds configurator definition.
526      *
527      * @param Definition $definition
528      * @param string     $variableName
529      *
530      * @return string
531      */
532     private function addServiceConfigurator(Definition $definition, $variableName = 'instance')
533     {
534         if (!$callable = $definition->getConfigurator()) {
535             return '';
536         }
537
538         if (is_array($callable)) {
539             if ($callable[0] instanceof Reference
540                 || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
541                 return sprintf("        %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
542             }
543
544             $class = $this->dumpValue($callable[0]);
545             // If the class is a string we can optimize call_user_func away
546             if (0 === strpos($class, "'") && false === strpos($class, '$')) {
547                 return sprintf("        %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName);
548             }
549
550             if (0 === strpos($class, 'new ')) {
551                 return sprintf("        (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
552             }
553
554             return sprintf("        call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
555         }
556
557         return sprintf("        %s(\$%s);\n", $callable, $variableName);
558     }
559
560     /**
561      * Adds a service.
562      *
563      * @param string     $id
564      * @param Definition $definition
565      *
566      * @return string
567      */
568     private function addService($id, Definition $definition)
569     {
570         $this->definitionVariables = new \SplObjectStorage();
571         $this->referenceVariables = array();
572         $this->variableCount = 0;
573
574         $return = array();
575
576         if ($definition->isSynthetic()) {
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);
580             $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\'));
581         } elseif ($definition->getFactory()) {
582             $factory = $definition->getFactory();
583             if (is_string($factory)) {
584                 $return[] = sprintf('@return object An instance returned by %s()', $factory);
585             } elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
586                 if (is_string($factory[0]) || $factory[0] instanceof Reference) {
587                     $return[] = sprintf('@return object An instance returned by %s::%s()', (string) $factory[0], $factory[1]);
588                 } elseif ($factory[0] instanceof Definition) {
589                     $return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]);
590                 }
591             }
592         }
593
594         if ($definition->isDeprecated()) {
595             if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
596                 $return[] = '';
597             }
598
599             $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id));
600         }
601
602         $return = str_replace("\n     * \n", "\n     *\n", implode("\n     * ", $return));
603         $return = $this->container->resolveEnvPlaceholders($return);
604
605         $shared = $definition->isShared() ? ' shared' : '';
606         $public = $definition->isPublic() ? 'public' : 'private';
607         $autowired = $definition->isAutowired() ? ' autowired' : '';
608
609         if ($definition->isLazy()) {
610             $lazyInitialization = '$lazyLoad = true';
611         } else {
612             $lazyInitialization = '';
613         }
614
615         // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
616         $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
617         $visibility = $isProxyCandidate ? 'public' : 'protected';
618         $methodName = $this->generateMethodName($id);
619         $code = <<<EOF
620
621     /*{$this->docStar}
622      * Gets the $public '$id'$shared$autowired service.
623      *
624      * $return
625      */
626     {$visibility} function {$methodName}($lazyInitialization)
627     {
628
629 EOF;
630
631         $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $methodName) : '';
632
633         if ($definition->isSynthetic()) {
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);
635         } else {
636             if ($definition->isDeprecated()) {
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         }
652
653         $this->definitionVariables = null;
654         $this->referenceVariables = null;
655
656         return $code;
657     }
658
659     /**
660      * Adds multiple services.
661      *
662      * @return string
663      */
664     private function addServices()
665     {
666         $publicServices = $privateServices = '';
667         $definitions = $this->container->getDefinitions();
668         ksort($definitions);
669         foreach ($definitions as $id => $definition) {
670             if ($definition->isPublic()) {
671                 $publicServices .= $this->addService($id, $definition);
672             } else {
673                 $privateServices .= $this->addService($id, $definition);
674             }
675         }
676
677         return $publicServices.$privateServices;
678     }
679
680     private function addNewInstance(Definition $definition, $return, $instantiation, $id)
681     {
682         $class = $this->dumpValue($definition->getClass());
683
684         $arguments = array();
685         foreach ($definition->getArguments() as $value) {
686             $arguments[] = $this->dumpValue($value);
687         }
688
689         if (null !== $definition->getFactory()) {
690             $callable = $definition->getFactory();
691             if (is_array($callable)) {
692                 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) {
693                     throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a'));
694                 }
695
696                 if ($callable[0] instanceof Reference
697                     || ($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) : '');
699                 }
700
701                 $class = $this->dumpValue($callable[0]);
702                 // If the class is a string we can optimize call_user_func away
703                 if (0 === strpos($class, "'") && false === strpos($class, '$')) {
704                     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));
706                     }
707
708                     return sprintf("        $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
709                 }
710
711                 if (0 === strpos($class, 'new ')) {
712                     return sprintf("        $return{$instantiation}(%s)->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
713                 }
714
715                 return sprintf("        $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
716             }
717
718             return sprintf("        $return{$instantiation}%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '');
719         }
720
721         if (false !== strpos($class, '$')) {
722             return sprintf("        \$class = %s;\n\n        $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
723         }
724
725         return sprintf("        $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments));
726     }
727
728     /**
729      * Adds the class headers.
730      *
731      * @param string $class     Class name
732      * @param string $baseClass The name of the base class
733      * @param string $namespace The class namespace
734      *
735      * @return string
736      */
737     private function startClass($class, $baseClass, $namespace)
738     {
739         $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
740         $namespaceLine = $namespace ? "\nnamespace $namespace;\n" : '';
741
742         return <<<EOF
743 <?php
744 $namespaceLine
745 use Symfony\Component\DependencyInjection\ContainerInterface;
746 use Symfony\Component\DependencyInjection\Container;
747 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
748 use Symfony\Component\DependencyInjection\Exception\LogicException;
749 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
750 $bagClass
751
752 /*{$this->docStar}
753  * $class.
754  *
755  * This class has been auto-generated
756  * by the Symfony Dependency Injection Component.
757  */
758 class $class extends $baseClass
759 {
760     private \$parameters;
761     private \$targetDirs = array();
762
763 EOF;
764     }
765
766     /**
767      * Adds the constructor.
768      *
769      * @return string
770      */
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
785 EOF;
786
787         $code .= $this->addMethodMap();
788         $code .= $this->addPrivateServices();
789         $code .= $this->addAliases();
790
791         $code .= <<<'EOF'
792     }
793
794 EOF;
795
796         return $code;
797     }
798
799     /**
800      * Adds the constructor for a frozen container.
801      *
802      * @return string
803      */
804     private function addFrozenConstructor()
805     {
806         $targetDirs = $this->exportTargetDirs();
807
808         $code = <<<EOF
809
810     /*{$this->docStar}
811      * Constructor.
812      */
813     public function __construct()
814     {{$targetDirs}
815 EOF;
816
817         if ($this->container->getParameterBag()->all()) {
818             $code .= "\n        \$this->parameters = \$this->getDefaultParameters();\n";
819         }
820
821         $code .= "\n        \$this->services = array();\n";
822         $code .= $this->addMethodMap();
823         $code .= $this->addPrivateServices();
824         $code .= $this->addAliases();
825
826         $code .= <<<'EOF'
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);
887         foreach ($definitions as $id => $definition) {
888             $code .= '            '.$this->export($id).' => '.$this->export($this->generateMethodName($id)).",\n";
889         }
890
891         return $code."        );\n";
892     }
893
894     /**
895      * Adds the privates property definition.
896      *
897      * @return string
898      */
899     private function addPrivateServices()
900     {
901         if (!$definitions = $this->container->getDefinitions()) {
902             return '';
903         }
904
905         $code = '';
906         ksort($definitions);
907         foreach ($definitions as $id => $definition) {
908             if (!$definition->isPublic()) {
909                 $code .= '            '.$this->export($id)." => true,\n";
910             }
911         }
912
913         if (empty($code)) {
914             return '';
915         }
916
917         $out = "        \$this->privates = array(\n";
918         $out .= $code;
919         $out .= "        );\n";
920
921         return $out;
922     }
923
924     /**
925      * Adds the aliases property definition.
926      *
927      * @return string
928      */
929     private function addAliases()
930     {
931         if (!$aliases = $this->container->getAliases()) {
932             return $this->container->isFrozen() ? "\n        \$this->aliases = array();\n" : '';
933         }
934
935         $code = "        \$this->aliases = array(\n";
936         ksort($aliases);
937         foreach ($aliases as $alias => $id) {
938             $id = (string) $id;
939             while (isset($aliases[$id])) {
940                 $id = (string) $aliases[$id];
941             }
942             $code .= '            '.$this->export($alias).' => '.$this->export($id).",\n";
943         }
944
945         return $code."        );\n";
946     }
947
948     /**
949      * Adds default parameters method.
950      *
951      * @return string
952      */
953     private function addDefaultParametersMethod()
954     {
955         if (!$this->container->getParameterBag()->all()) {
956             return '';
957         }
958
959         $php = array();
960         $dynamicPhp = array();
961
962         foreach ($this->container->getParameterBag()->all() as $key => $value) {
963             if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) {
964                 throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey));
965             }
966             $export = $this->exportParameters(array($value));
967             $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2);
968
969             if (preg_match("/\\\$this->(?:getEnv\('\w++'\)|targetDirs\[\d++\])/", $export[1])) {
970                 $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]);
971             } else {
972                 $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
973             }
974         }
975         $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8));
976
977         $code = '';
978         if ($this->container->isFrozen()) {
979             $code .= <<<'EOF'
980
981     /**
982      * {@inheritdoc}
983      */
984     public function getParameter($name)
985     {
986         $name = strtolower($name);
987
988         if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) {
989             throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
990         }
991         if (isset($this->loadedDynamicParameters[$name])) {
992             return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
993         }
994
995         return $this->parameters[$name];
996     }
997
998     /**
999      * {@inheritdoc}
1000      */
1001     public function hasParameter($name)
1002     {
1003         $name = strtolower($name);
1004
1005         return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]);
1006     }
1007
1008     /**
1009      * {@inheritdoc}
1010      */
1011     public function setParameter($name, $value)
1012     {
1013         throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
1014     }
1015
1016     /**
1017      * {@inheritdoc}
1018      */
1019     public function getParameterBag()
1020     {
1021         if (null === $this->parameterBag) {
1022             $parameters = $this->parameters;
1023             foreach ($this->loadedDynamicParameters as $name => $loaded) {
1024                 $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
1025             }
1026             $this->parameterBag = new FrozenParameterBag($parameters);
1027         }
1028
1029         return $this->parameterBag;
1030     }
1031
1032 EOF;
1033             if ('' === $this->docStar) {
1034                 $code = str_replace('/**', '/*', $code);
1035             }
1036
1037             if ($dynamicPhp) {
1038                 $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8);
1039                 $getDynamicParameter = <<<'EOF'
1040         switch ($name) {
1041 %s
1042             default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name));
1043         }
1044         $this->loadedDynamicParameters[$name] = true;
1045
1046         return $this->dynamicParameters[$name] = $value;
1047 EOF;
1048                 $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp));
1049             } else {
1050                 $loadedDynamicParameters = 'array()';
1051                 $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));';
1052             }
1053
1054             $code .= <<<EOF
1055
1056     private \$loadedDynamicParameters = {$loadedDynamicParameters};
1057     private \$dynamicParameters = array();
1058
1059     /*{$this->docStar}
1060      * Computes a dynamic parameter.
1061      *
1062      * @param string The name of the dynamic parameter to load
1063      *
1064      * @return mixed The value of the dynamic parameter
1065      *
1066      * @throws InvalidArgumentException When the dynamic parameter does not exist
1067      */
1068     private function getDynamicParameter(\$name)
1069     {
1070 {$getDynamicParameter}
1071     }
1072
1073 EOF;
1074         } elseif ($dynamicPhp) {
1075             throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.');
1076         }
1077
1078         $code .= <<<EOF
1079
1080     /*{$this->docStar}
1081      * Gets the default parameters.
1082      *
1083      * @return array An array of the default parameters
1084      */
1085     protected function getDefaultParameters()
1086     {
1087         return $parameters;
1088     }
1089
1090 EOF;
1091
1092         return $code;
1093     }
1094
1095     /**
1096      * Exports parameters.
1097      *
1098      * @param array  $parameters
1099      * @param string $path
1100      * @param int    $indent
1101      *
1102      * @return string
1103      *
1104      * @throws InvalidArgumentException
1105      */
1106     private function exportParameters(array $parameters, $path = '', $indent = 12)
1107     {
1108         $php = array();
1109         foreach ($parameters as $key => $value) {
1110             if (is_array($value)) {
1111                 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
1112             } 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));
1114             } 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));
1116             } elseif ($value instanceof Reference) {
1117                 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key));
1118             } elseif ($value instanceof Expression) {
1119                 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key));
1120             } else {
1121                 $value = $this->export($value);
1122             }
1123
1124             $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value);
1125         }
1126
1127         return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
1128     }
1129
1130     /**
1131      * Ends the class definition.
1132      *
1133      * @return string
1134      */
1135     private function endClass()
1136     {
1137         return <<<'EOF'
1138 }
1139
1140 EOF;
1141     }
1142
1143     /**
1144      * Wraps the service conditionals.
1145      *
1146      * @param string $value
1147      * @param string $code
1148      *
1149      * @return string
1150      */
1151     private function wrapServiceConditionals($value, $code)
1152     {
1153         if (!$services = ContainerBuilder::getServiceConditionals($value)) {
1154             return $code;
1155         }
1156
1157         $conditions = array();
1158         foreach ($services as $service) {
1159             if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) {
1160                 continue;
1161             }
1162
1163             $conditions[] = sprintf("\$this->has('%s')", $service);
1164         }
1165
1166         if (!$conditions) {
1167             return $code;
1168         }
1169
1170         // re-indent the wrapped code
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     }
1175
1176     /**
1177      * Builds service calls from arguments.
1178      *
1179      * @param array $arguments
1180      * @param array &$calls    By reference
1181      * @param array &$behavior By reference
1182      */
1183     private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
1184     {
1185         foreach ($arguments as $argument) {
1186             if (is_array($argument)) {
1187                 $this->getServiceCallsFromArguments($argument, $calls, $behavior);
1188             } elseif ($argument instanceof Reference) {
1189                 $id = (string) $argument;
1190
1191                 if (!isset($calls[$id])) {
1192                     $calls[$id] = 0;
1193                 }
1194                 if (!isset($behavior[$id])) {
1195                     $behavior[$id] = $argument->getInvalidBehavior();
1196                 } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
1197                     $behavior[$id] = $argument->getInvalidBehavior();
1198                 }
1199
1200                 ++$calls[$id];
1201             }
1202         }
1203     }
1204
1205     /**
1206      * Returns the inline definition.
1207      *
1208      * @param Definition $definition
1209      *
1210      * @return array
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) {
1242             if (is_array($argument)) {
1243                 $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
1244             } elseif ($argument instanceof Definition) {
1245                 $definitions = array_merge(
1246                     $definitions,
1247                     $this->getInlinedDefinitions($argument),
1248                     array($argument)
1249                 );
1250             }
1251         }
1252
1253         return $definitions;
1254     }
1255
1256     /**
1257      * Checks if a service id has a reference.
1258      *
1259      * @param string $id
1260      * @param array  $arguments
1261      * @param bool   $deep
1262      * @param array  $visited
1263      *
1264      * @return bool
1265      */
1266     private function hasReference($id, array $arguments, $deep = false, array &$visited = array())
1267     {
1268         foreach ($arguments as $argument) {
1269             if (is_array($argument)) {
1270                 if ($this->hasReference($id, $argument, $deep, $visited)) {
1271                     return true;
1272                 }
1273             } elseif ($argument instanceof Reference) {
1274                 $argumentId = (string) $argument;
1275                 if ($id === $argumentId) {
1276                     return true;
1277                 }
1278
1279                 if ($deep && !isset($visited[$argumentId]) && 'service_container' !== $argumentId) {
1280                     $visited[$argumentId] = true;
1281
1282                     $service = $this->container->getDefinition($argumentId);
1283
1284                     // if the proxy manager is enabled, disable searching for references in lazy services,
1285                     // as these services will be instantiated lazily and don't have direct related references.
1286                     if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) {
1287                         continue;
1288                     }
1289
1290                     $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
1291
1292                     if ($this->hasReference($id, $arguments, $deep, $visited)) {
1293                         return true;
1294                     }
1295                 }
1296             }
1297         }
1298
1299         return false;
1300     }
1301
1302     /**
1303      * Dumps values.
1304      *
1305      * @param mixed $value
1306      * @param bool  $interpolate
1307      *
1308      * @return string
1309      *
1310      * @throws RuntimeException
1311      */
1312     private function dumpValue($value, $interpolate = true)
1313     {
1314         if (is_array($value)) {
1315             $code = array();
1316             foreach ($value as $k => $v) {
1317                 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1318             }
1319
1320             return sprintf('array(%s)', implode(', ', $code));
1321         } elseif ($value instanceof Definition) {
1322             if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
1323                 return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
1324             }
1325             if ($value->getMethodCalls()) {
1326                 throw new RuntimeException('Cannot dump definitions which have method calls.');
1327             }
1328             if ($value->getProperties()) {
1329                 throw new RuntimeException('Cannot dump definitions which have properties.');
1330             }
1331             if (null !== $value->getConfigurator()) {
1332                 throw new RuntimeException('Cannot dump definitions which have a configurator.');
1333             }
1334
1335             $arguments = array();
1336             foreach ($value->getArguments() as $argument) {
1337                 $arguments[] = $this->dumpValue($argument);
1338             }
1339
1340             if (null !== $value->getFactory()) {
1341                 $factory = $value->getFactory();
1342
1343                 if (is_string($factory)) {
1344                     return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments));
1345                 }
1346
1347                 if (is_array($factory)) {
1348                     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'));
1350                     }
1351
1352                     if (is_string($factory[0])) {
1353                         return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments));
1354                     }
1355
1356                     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) : '');
1358                     }
1359
1360                     if ($factory[0] instanceof Reference) {
1361                         return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
1362                     }
1363                 }
1364
1365                 throw new RuntimeException('Cannot dump definition because of invalid factory');
1366             }
1367
1368             $class = $value->getClass();
1369             if (null === $class) {
1370                 throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
1371             }
1372
1373             return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments));
1374         } elseif ($value instanceof Variable) {
1375             return '$'.$value;
1376         } elseif ($value instanceof Reference) {
1377             if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
1378                 return $this->dumpValue($this->referenceVariables[$id], $interpolate);
1379             }
1380
1381             return $this->getServiceCall((string) $value, $value);
1382         } elseif ($value instanceof Expression) {
1383             return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container'));
1384         } elseif ($value instanceof Parameter) {
1385             return $this->dumpParameter($value);
1386         } elseif (true === $interpolate && is_string($value)) {
1387             if (preg_match('/^%([^%]+)%$/', $value, $match)) {
1388                 // we do this to deal with non string values (Boolean, integer, ...)
1389                 // the preg_replace_callback converts them to strings
1390                 return $this->dumpParameter(strtolower($match[1]));
1391             } else {
1392                 $replaceParameters = function ($match) {
1393                     return "'.".$this->dumpParameter(strtolower($match[2])).".'";
1394                 };
1395
1396                 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
1397
1398                 return $code;
1399             }
1400         } elseif (is_object($value) || is_resource($value)) {
1401             throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
1402         }
1403
1404         return $this->export($value);
1405     }
1406
1407     /**
1408      * Dumps a string to a literal (aka PHP Code) class value.
1409      *
1410      * @param string $class
1411      *
1412      * @return string
1413      *
1414      * @throws RuntimeException
1415      */
1416     private function dumpLiteralClass($class)
1417     {
1418         if (false !== strpos($class, '$')) {
1419             return sprintf('${($_ = %s) && false ?: "_"}', $class);
1420         }
1421         if (0 !== 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)) {
1422             throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a'));
1423         }
1424
1425         $class = substr(str_replace('\\\\', '\\', $class), 1, -1);
1426
1427         return 0 === strpos($class, '\\') ? $class : '\\'.$class;
1428     }
1429
1430     /**
1431      * Dumps a parameter.
1432      *
1433      * @param string $name
1434      *
1435      * @return string
1436      */
1437     private function dumpParameter($name)
1438     {
1439         if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
1440             return $this->dumpValue($this->container->getParameter($name), false);
1441         }
1442
1443         return sprintf("\$this->getParameter('%s')", strtolower($name));
1444     }
1445
1446     /**
1447      * Gets a service call.
1448      *
1449      * @param string    $id
1450      * @param Reference $reference
1451      *
1452      * @return string
1453      */
1454     private function getServiceCall($id, Reference $reference = null)
1455     {
1456         while ($this->container->hasAlias($id)) {
1457             $id = (string) $this->container->getAlias($id);
1458         }
1459
1460         if ('service_container' === $id) {
1461             return '$this';
1462         }
1463
1464         if ($this->container->hasDefinition($id) && !$this->container->getDefinition($id)->isPublic()) {
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
1466
1467             return "\${(\$_ = isset(\$this->services['$id']) ? \$this->services['$id'] : \$this->{$this->generateMethodName($id)}()) && false ?: '_'}";
1468         }
1469         if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
1470             return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
1471         }
1472
1473         return sprintf('$this->get(\'%s\')', $id);
1474     }
1475
1476     /**
1477      * Initializes the method names map to avoid conflicts with the Container methods.
1478      *
1479      * @param string $class the container base class
1480      */
1481     private function initializeMethodNamesMap($class)
1482     {
1483         $this->serviceIdToMethodNameMap = array();
1484         $this->usedMethodNames = array();
1485
1486         try {
1487             $reflectionClass = new \ReflectionClass($class);
1488             foreach ($reflectionClass->getMethods() as $method) {
1489                 $this->usedMethodNames[strtolower($method->getName())] = true;
1490             }
1491         } catch (\ReflectionException $e) {
1492         }
1493     }
1494
1495     /**
1496      * Convert a service id to a valid PHP method name.
1497      *
1498      * @param string $id
1499      *
1500      * @return string
1501      *
1502      * @throws InvalidArgumentException
1503      */
1504     private function generateMethodName($id)
1505     {
1506         if (isset($this->serviceIdToMethodNameMap[$id])) {
1507             return $this->serviceIdToMethodNameMap[$id];
1508         }
1509
1510         $name = Container::camelize($id);
1511         $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name);
1512         $methodName = 'get'.$name.'Service';
1513         $suffix = 1;
1514
1515         while (isset($this->usedMethodNames[strtolower($methodName)])) {
1516             ++$suffix;
1517             $methodName = 'get'.$name.$suffix.'Service';
1518         }
1519
1520         $this->serviceIdToMethodNameMap[$id] = $methodName;
1521         $this->usedMethodNames[strtolower($methodName)] = true;
1522
1523         return $methodName;
1524     }
1525
1526     /**
1527      * Returns the next name to use.
1528      *
1529      * @return string
1530      */
1531     private function getNextVariableName()
1532     {
1533         $firstChars = self::FIRST_CHARS;
1534         $firstCharsLength = strlen($firstChars);
1535         $nonFirstChars = self::NON_FIRST_CHARS;
1536         $nonFirstCharsLength = strlen($nonFirstChars);
1537
1538         while (true) {
1539             $name = '';
1540             $i = $this->variableCount;
1541
1542             if ('' === $name) {
1543                 $name .= $firstChars[$i % $firstCharsLength];
1544                 $i = (int) ($i / $firstCharsLength);
1545             }
1546
1547             while ($i > 0) {
1548                 --$i;
1549                 $name .= $nonFirstChars[$i % $nonFirstCharsLength];
1550                 $i = (int) ($i / $nonFirstCharsLength);
1551             }
1552
1553             ++$this->variableCount;
1554
1555             // check that the name is not reserved
1556             if (in_array($name, $this->reservedVariables, true)) {
1557                 continue;
1558             }
1559
1560             return $name;
1561         }
1562     }
1563
1564     private function getExpressionLanguage()
1565     {
1566         if (null === $this->expressionLanguage) {
1567             if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
1568                 throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
1569             }
1570             $providers = $this->container->getExpressionLanguageProviders();
1571             $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
1572                 $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null;
1573
1574                 if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) {
1575                     return $this->getServiceCall($id);
1576                 }
1577
1578                 return sprintf('$this->get(%s)', $arg);
1579             });
1580
1581             if ($this->container->isTrackingResources()) {
1582                 foreach ($providers as $provider) {
1583                     $this->container->addObjectResource($provider);
1584                 }
1585             }
1586         }
1587
1588         return $this->expressionLanguage;
1589     }
1590
1591     private function exportTargetDirs()
1592     {
1593         return null === $this->targetDirRegex ? '' : <<<EOF
1594
1595         \$dir = __DIR__;
1596         for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
1597             \$this->targetDirs[\$i] = \$dir = dirname(\$dir);
1598         }
1599 EOF;
1600     }
1601
1602     private function export($value)
1603     {
1604         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])).'.' : '';
1606             $suffix = $matches[0][1] + strlen($matches[0][0]);
1607             $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix)) : '';
1608             $dirname = '__DIR__';
1609
1610             if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) {
1611                 $dirname = sprintf('$this->targetDirs[%d]', $offset);
1612             }
1613
1614             if ($prefix || $suffix) {
1615                 return sprintf('(%s%s%s)', $prefix, $dirname, $suffix);
1616             }
1617
1618             return $dirname;
1619         }
1620
1621         return $this->doExport($value);
1622     }
1623
1624     private function doExport($value)
1625     {
1626         $export = var_export($value, true);
1627
1628         if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('%s').'")) {
1629             $export = $resolvedExport;
1630             if ("'" === $export[1]) {
1631                 $export = substr($export, 3);
1632             }
1633             if (".''" === substr($export, -3)) {
1634                 $export = substr($export, 0, -3);
1635             }
1636         }
1637
1638         return $export;
1639     }
1640 }