commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / vendor / symfony / dependency-injection / Symfony / Component / DependencyInjection / 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\InvalidArgumentException;
22 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
23 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
24 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
25 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
26 use Symfony\Component\HttpKernel\Kernel;
27
28 /**
29 * PhpDumper dumps a service container as a PHP class.
30 *
31 * @author Fabien Potencier <fabien@symfony.com>
32 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
33 */
34 class PhpDumper extends Dumper
35 {
36 /**
37 * Characters that might appear in the generated variable name as first character.
38 *
39 * @var string
40 */
41 const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
42
43 /**
44 * Characters that might appear in the generated variable name as any but the first character.
45 *
46 * @var string
47 */
48 const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
49
50 private $inlinedDefinitions;
51 private $definitionVariables;
52 private $referenceVariables;
53 private $variableCount;
54 private $reservedVariables = array('instance', 'class');
55 private $targetDirRegex;
56 private $targetDirMaxMatches;
57 private $docStar;
58
59 /**
60 * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
61 */
62 private $proxyDumper;
63
64 /**
65 * {@inheritdoc}
66 */
67 public function __construct(ContainerBuilder $container)
68 {
69 parent::__construct($container);
70
71 $this->inlinedDefinitions = new \SplObjectStorage();
72 }
73
74 /**
75 * Sets the dumper to be used when dumping proxies in the generated container.
76 *
77 * @param ProxyDumper $proxyDumper
78 */
79 public function setProxyDumper(ProxyDumper $proxyDumper)
80 {
81 $this->proxyDumper = $proxyDumper;
82 }
83
84 /**
85 * Dumps the service container as a PHP class.
86 *
87 * Available options:
88 *
89 * * class: The class name
90 * * base_class: The base class name
91 *
92 * @param array $options An array of options
93 *
94 * @return string A PHP class representing of the service container
95 */
96 public function dump(array $options = array())
97 {
98 $this->targetDirRegex = null;
99 $options = array_merge(array(
100 'class' => 'ProjectServiceContainer',
101 'base_class' => 'Container',
102 'debug' => true,
103 ), $options);
104 $this->docStar = $options['debug'] ? '*' : '';
105
106 if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
107 // Build a regexp where the first root dirs are mandatory,
108 // but every other sub-dir is optional up to the full path in $dir
109 // Mandate at least 2 root dirs and not more that 5 optional dirs.
110
111 $dir = explode(DIRECTORY_SEPARATOR, realpath($dir));
112 $i = count($dir);
113
114 if (3 <= $i) {
115 $regex = '';
116 $lastOptionalDir = $i > 8 ? $i - 5 : 3;
117 $this->targetDirMaxMatches = $i - $lastOptionalDir;
118
119 while (--$i >= $lastOptionalDir) {
120 $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex);
121 }
122
123 do {
124 $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex;
125 } while (0 < --$i);
126
127 $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#';
128 }
129 }
130
131 $code = $this->startClass($options['class'], $options['base_class']);
132
133 if ($this->container->isFrozen()) {
134 $code .= $this->addFrozenConstructor();
135 } else {
136 $code .= $this->addConstructor();
137 }
138
139 $code .=
140 $this->addServices().
141 $this->addDefaultParametersMethod().
142 $this->endClass().
143 $this->addProxyClasses()
144 ;
145 $this->targetDirRegex = null;
146
147 return $code;
148 }
149
150 /**
151 * Retrieves the currently set proxy dumper or instantiates one.
152 *
153 * @return ProxyDumper
154 */
155 private function getProxyDumper()
156 {
157 if (!$this->proxyDumper) {
158 $this->proxyDumper = new NullDumper();
159 }
160
161 return $this->proxyDumper;
162 }
163
164 /**
165 * Generates Service local temp variables.
166 *
167 * @param string $cId
168 * @param string $definition
169 *
170 * @return string
171 */
172 private function addServiceLocalTempVariables($cId, $definition)
173 {
174 static $template = " \$%s = %s;\n";
175
176 $localDefinitions = array_merge(
177 array($definition),
178 $this->getInlinedDefinitions($definition)
179 );
180
181 $calls = $behavior = array();
182 foreach ($localDefinitions as $iDefinition) {
183 $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
184 $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
185 $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
186 }
187
188 $code = '';
189 foreach ($calls as $id => $callCount) {
190 if ('service_container' === $id || $id === $cId) {
191 continue;
192 }
193
194 if ($callCount > 1) {
195 $name = $this->getNextVariableName();
196 $this->referenceVariables[$id] = new Variable($name);
197
198 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
199 $code .= sprintf($template, $name, $this->getServiceCall($id));
200 } else {
201 $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
202 }
203 }
204 }
205
206 if ('' !== $code) {
207 $code .= "\n";
208 }
209
210 return $code;
211 }
212
213 /**
214 * Generates code for the proxies to be attached after the container class.
215 *
216 * @return string
217 */
218 private function addProxyClasses()
219 {
220 /* @var $definitions Definition[] */
221 $definitions = array_filter(
222 $this->container->getDefinitions(),
223 array($this->getProxyDumper(), 'isProxyCandidate')
224 );
225 $code = '';
226 $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
227
228 foreach ($definitions as $definition) {
229 $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition);
230 if ($strip) {
231 $proxyCode = "<?php\n".$proxyCode;
232 $proxyCode = substr(Kernel::stripComments($proxyCode), 5);
233 }
234 $code .= $proxyCode;
235 }
236
237 return $code;
238 }
239
240 /**
241 * Generates the require_once statement for service includes.
242 *
243 * @param string $id The service id
244 * @param Definition $definition
245 *
246 * @return string
247 */
248 private function addServiceInclude($id, $definition)
249 {
250 $template = " require_once %s;\n";
251 $code = '';
252
253 if (null !== $file = $definition->getFile()) {
254 $code .= sprintf($template, $this->dumpValue($file));
255 }
256
257 foreach ($this->getInlinedDefinitions($definition) as $definition) {
258 if (null !== $file = $definition->getFile()) {
259 $code .= sprintf($template, $this->dumpValue($file));
260 }
261 }
262
263 if ('' !== $code) {
264 $code .= "\n";
265 }
266
267 return $code;
268 }
269
270 /**
271 * Generates the inline definition of a service.
272 *
273 * @param string $id
274 * @param Definition $definition
275 *
276 * @return string
277 *
278 * @throws RuntimeException When the factory definition is incomplete
279 * @throws ServiceCircularReferenceException When a circular reference is detected
280 */
281 private function addServiceInlinedDefinitions($id, $definition)
282 {
283 $code = '';
284 $variableMap = $this->definitionVariables;
285 $nbOccurrences = new \SplObjectStorage();
286 $processed = new \SplObjectStorage();
287 $inlinedDefinitions = $this->getInlinedDefinitions($definition);
288
289 foreach ($inlinedDefinitions as $definition) {
290 if (false === $nbOccurrences->contains($definition)) {
291 $nbOccurrences->offsetSet($definition, 1);
292 } else {
293 $i = $nbOccurrences->offsetGet($definition);
294 $nbOccurrences->offsetSet($definition, $i + 1);
295 }
296 }
297
298 foreach ($inlinedDefinitions as $sDefinition) {
299 if ($processed->contains($sDefinition)) {
300 continue;
301 }
302 $processed->offsetSet($sDefinition);
303
304 $class = $this->dumpValue($sDefinition->getClass());
305 if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
306 $name = $this->getNextVariableName();
307 $variableMap->offsetSet($sDefinition, new Variable($name));
308
309 // a construct like:
310 // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
311 // this is an indication for a wrong implementation, you can circumvent this problem
312 // by setting up your service structure like this:
313 // $b = new ServiceB();
314 // $a = new ServiceA(ServiceB $b);
315 // $b->setServiceA(ServiceA $a);
316 if ($this->hasReference($id, $sDefinition->getArguments())) {
317 throw new ServiceCircularReferenceException($id, array($id));
318 }
319
320 $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
321
322 if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
323 $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
324 $code .= $this->addServiceProperties(null, $sDefinition, $name);
325 $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
326 }
327
328 $code .= "\n";
329 }
330 }
331
332 return $code;
333 }
334
335 /**
336 * Adds the service return statement.
337 *
338 * @param string $id Service id
339 * @param Definition $definition
340 *
341 * @return string
342 */
343 private function addServiceReturn($id, $definition)
344 {
345 if ($this->isSimpleInstance($id, $definition)) {
346 return " }\n";
347 }
348
349 return "\n return \$instance;\n }\n";
350 }
351
352 /**
353 * Generates the service instance.
354 *
355 * @param string $id
356 * @param Definition $definition
357 *
358 * @return string
359 *
360 * @throws InvalidArgumentException
361 * @throws RuntimeException
362 */
363 private function addServiceInstance($id, $definition)
364 {
365 $class = $definition->getClass();
366
367 if ('\\' === substr($class, 0, 1)) {
368 $class = substr($class, 1);
369 }
370
371 $class = $this->dumpValue($class);
372
373 if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
374 throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
375 }
376
377 $simple = $this->isSimpleInstance($id, $definition);
378 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
379 $instantiation = '';
380
381 if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
382 $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
383 } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
384 $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
385 } elseif (!$simple) {
386 $instantiation = '$instance';
387 }
388
389 $return = '';
390 if ($simple) {
391 $return = 'return ';
392 } else {
393 $instantiation .= ' = ';
394 }
395
396 $code = $this->addNewInstance($id, $definition, $return, $instantiation);
397
398 if (!$simple) {
399 $code .= "\n";
400 }
401
402 return $code;
403 }
404
405 /**
406 * Checks if the definition is a simple instance.
407 *
408 * @param string $id
409 * @param Definition $definition
410 *
411 * @return bool
412 */
413 private function isSimpleInstance($id, $definition)
414 {
415 foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
416 if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
417 continue;
418 }
419
420 if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
421 return false;
422 }
423 }
424
425 return true;
426 }
427
428 /**
429 * Adds method calls to a service definition.
430 *
431 * @param string $id
432 * @param Definition $definition
433 * @param string $variableName
434 *
435 * @return string
436 */
437 private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
438 {
439 $calls = '';
440 foreach ($definition->getMethodCalls() as $call) {
441 $arguments = array();
442 foreach ($call[1] as $value) {
443 $arguments[] = $this->dumpValue($value);
444 }
445
446 $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
447 }
448
449 return $calls;
450 }
451
452 private function addServiceProperties($id, $definition, $variableName = 'instance')
453 {
454 $code = '';
455 foreach ($definition->getProperties() as $name => $value) {
456 $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
457 }
458
459 return $code;
460 }
461
462 /**
463 * Generates the inline definition setup.
464 *
465 * @param string $id
466 * @param Definition $definition
467 *
468 * @return string
469 *
470 * @throws ServiceCircularReferenceException when the container contains a circular reference
471 */
472 private function addServiceInlinedDefinitionsSetup($id, $definition)
473 {
474 $this->referenceVariables[$id] = new Variable('instance');
475
476 $code = '';
477 $processed = new \SplObjectStorage();
478 foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
479 if ($processed->contains($iDefinition)) {
480 continue;
481 }
482 $processed->offsetSet($iDefinition);
483
484 if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
485 continue;
486 }
487
488 // if the instance is simple, the return statement has already been generated
489 // so, the only possible way to get there is because of a circular reference
490 if ($this->isSimpleInstance($id, $definition)) {
491 throw new ServiceCircularReferenceException($id, array($id));
492 }
493
494 $name = (string) $this->definitionVariables->offsetGet($iDefinition);
495 $code .= $this->addServiceMethodCalls(null, $iDefinition, $name);
496 $code .= $this->addServiceProperties(null, $iDefinition, $name);
497 $code .= $this->addServiceConfigurator(null, $iDefinition, $name);
498 }
499
500 if ('' !== $code) {
501 $code .= "\n";
502 }
503
504 return $code;
505 }
506
507 /**
508 * Adds configurator definition.
509 *
510 * @param string $id
511 * @param Definition $definition
512 * @param string $variableName
513 *
514 * @return string
515 */
516 private function addServiceConfigurator($id, $definition, $variableName = 'instance')
517 {
518 if (!$callable = $definition->getConfigurator()) {
519 return '';
520 }
521
522 if (is_array($callable)) {
523 if ($callable[0] instanceof Reference) {
524 return sprintf(" %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $callable[1], $variableName);
525 }
526
527 return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
528 }
529
530 return sprintf(" %s(\$%s);\n", $callable, $variableName);
531 }
532
533 /**
534 * Adds a service.
535 *
536 * @param string $id
537 * @param Definition $definition
538 *
539 * @return string
540 */
541 private function addService($id, $definition)
542 {
543 $this->definitionVariables = new \SplObjectStorage();
544 $this->referenceVariables = array();
545 $this->variableCount = 0;
546
547 $return = array();
548
549 if ($definition->isSynthetic()) {
550 $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
551 } elseif ($class = $definition->getClass()) {
552 $return[] = sprintf('@return %s A %s instance.', 0 === strpos($class, '%') ? 'object' : '\\'.ltrim($class, '\\'), ltrim($class, '\\'));
553 } elseif ($definition->getFactoryClass()) {
554 $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
555 } elseif ($definition->getFactoryService()) {
556 $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
557 }
558
559 $scope = $definition->getScope();
560 if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
561 if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
562 $return[] = '';
563 }
564 $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
565 }
566
567 $return = implode("\n * ", $return);
568
569 $doc = '';
570 if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
571 $doc .= <<<'EOF'
572
573 *
574 * This service is shared.
575 * This method always returns the same instance of the service.
576 EOF;
577 }
578
579 if (!$definition->isPublic()) {
580 $doc .= <<<'EOF'
581
582 *
583 * This service is private.
584 * If you want to be able to request this service from the container directly,
585 * make it public, otherwise you might end up with broken code.
586 EOF;
587 }
588
589 if ($definition->isLazy()) {
590 $lazyInitialization = '$lazyLoad = true';
591 $lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *";
592 } else {
593 $lazyInitialization = '';
594 $lazyInitializationDoc = '';
595 }
596
597 // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
598 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
599 $visibility = $isProxyCandidate ? 'public' : 'protected';
600 $code = <<<EOF
601
602 /*{$this->docStar}
603 * Gets the '$id' service.$doc
604 *$lazyInitializationDoc
605 * $return
606 */
607 {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
608 {
609
610 EOF;
611
612 $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
613
614 if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
615 $code .= <<<EOF
616 if (!isset(\$this->scopedServices['$scope'])) {
617 throw new InactiveScopeException('$id', '$scope');
618 }
619
620
621 EOF;
622 }
623
624 if ($definition->isSynthetic()) {
625 $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);
626 } else {
627 $code .=
628 $this->addServiceInclude($id, $definition).
629 $this->addServiceLocalTempVariables($id, $definition).
630 $this->addServiceInlinedDefinitions($id, $definition).
631 $this->addServiceInstance($id, $definition).
632 $this->addServiceInlinedDefinitionsSetup($id, $definition).
633 $this->addServiceMethodCalls($id, $definition).
634 $this->addServiceProperties($id, $definition).
635 $this->addServiceConfigurator($id, $definition).
636 $this->addServiceReturn($id, $definition)
637 ;
638 }
639
640 $this->definitionVariables = null;
641 $this->referenceVariables = null;
642
643 return $code;
644 }
645
646 /**
647 * Adds multiple services.
648 *
649 * @return string
650 */
651 private function addServices()
652 {
653 $publicServices = $privateServices = $synchronizers = '';
654 $definitions = $this->container->getDefinitions();
655 ksort($definitions);
656 foreach ($definitions as $id => $definition) {
657 if ($definition->isPublic()) {
658 $publicServices .= $this->addService($id, $definition);
659 } else {
660 $privateServices .= $this->addService($id, $definition);
661 }
662
663 $synchronizers .= $this->addServiceSynchronizer($id, $definition);
664 }
665
666 return $publicServices.$synchronizers.$privateServices;
667 }
668
669 /**
670 * Adds synchronizer methods.
671 *
672 * @param string $id A service identifier
673 * @param Definition $definition A Definition instance
674 *
675 * @return string|null
676 */
677 private function addServiceSynchronizer($id, Definition $definition)
678 {
679 if (!$definition->isSynchronized()) {
680 return;
681 }
682
683 $code = '';
684 foreach ($this->container->getDefinitions() as $definitionId => $definition) {
685 foreach ($definition->getMethodCalls() as $call) {
686 foreach ($call[1] as $argument) {
687 if ($argument instanceof Reference && $id == (string) $argument) {
688 $arguments = array();
689 foreach ($call[1] as $value) {
690 $arguments[] = $this->dumpValue($value);
691 }
692
693 $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
694
695 $code .= <<<EOF
696 if (\$this->initialized('$definitionId')) {
697 $call
698 }
699
700 EOF;
701 }
702 }
703 }
704 }
705
706 if (!$code) {
707 return;
708 }
709
710 return <<<EOF
711
712 /*{$this->docStar}
713 * Updates the '$id' service.
714 */
715 protected function synchronize{$this->camelize($id)}Service()
716 {
717 $code }
718
719 EOF;
720 }
721
722 private function addNewInstance($id, Definition $definition, $return, $instantiation)
723 {
724 $class = $this->dumpValue($definition->getClass());
725
726 $arguments = array();
727 foreach ($definition->getArguments() as $value) {
728 $arguments[] = $this->dumpValue($value);
729 }
730
731 if (null !== $definition->getFactoryMethod()) {
732 if (null !== $definition->getFactoryClass()) {
733 return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
734 }
735
736 if (null !== $definition->getFactoryService()) {
737 return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
738 }
739
740 throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
741 }
742
743 if (false !== strpos($class, '$')) {
744 return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
745 }
746
747 return sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
748 }
749
750 /**
751 * Adds the class headers.
752 *
753 * @param string $class Class name
754 * @param string $baseClass The name of the base class
755 *
756 * @return string
757 */
758 private function startClass($class, $baseClass)
759 {
760 $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
761
762 return <<<EOF
763 <?php
764
765 use Symfony\Component\DependencyInjection\ContainerInterface;
766 use Symfony\Component\DependencyInjection\Container;
767 use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
768 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
769 use Symfony\Component\DependencyInjection\Exception\LogicException;
770 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
771 $bagClass
772
773 /*{$this->docStar}
774 * $class.
775 *
776 * This class has been auto-generated
777 * by the Symfony Dependency Injection Component.
778 */
779 class $class extends $baseClass
780 {
781 private \$parameters;
782 private \$targetDirs = array();
783
784 EOF;
785 }
786
787 /**
788 * Adds the constructor.
789 *
790 * @return string
791 */
792 private function addConstructor()
793 {
794 $targetDirs = $this->exportTargetDirs();
795 $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
796
797 $code = <<<EOF
798
799 /*{$this->docStar}
800 * Constructor.
801 */
802 public function __construct()
803 {{$targetDirs}
804 parent::__construct($arguments);
805
806 EOF;
807
808 if (count($scopes = $this->container->getScopes()) > 0) {
809 $code .= "\n";
810 $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
811 $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren()).";\n";
812 }
813
814 $code .= $this->addMethodMap();
815 $code .= $this->addAliases();
816
817 $code .= <<<'EOF'
818 }
819
820 EOF;
821
822 return $code;
823 }
824
825 /**
826 * Adds the constructor for a frozen container.
827 *
828 * @return string
829 */
830 private function addFrozenConstructor()
831 {
832 $targetDirs = $this->exportTargetDirs();
833
834 $code = <<<EOF
835
836 /*{$this->docStar}
837 * Constructor.
838 */
839 public function __construct()
840 {{$targetDirs}
841 EOF;
842
843 if ($this->container->getParameterBag()->all()) {
844 $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
845 }
846
847 $code .= <<<'EOF'
848
849 $this->services =
850 $this->scopedServices =
851 $this->scopeStacks = array();
852 EOF;
853
854 $code .= "\n";
855 if (count($scopes = $this->container->getScopes()) > 0) {
856 $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
857 $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren()).";\n";
858 } else {
859 $code .= " \$this->scopes = array();\n";
860 $code .= " \$this->scopeChildren = array();\n";
861 }
862
863 $code .= $this->addMethodMap();
864 $code .= $this->addAliases();
865
866 $code .= <<<'EOF'
867 }
868
869 EOF;
870
871 return $code;
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 .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
889 }
890
891 return $code." );\n";
892 }
893
894 /**
895 * Adds the aliases property definition.
896 *
897 * @return string
898 */
899 private function addAliases()
900 {
901 if (!$aliases = $this->container->getAliases()) {
902 if ($this->container->isFrozen()) {
903 return "\n \$this->aliases = array();\n";
904 } else {
905 return '';
906 }
907 }
908
909 $code = " \$this->aliases = array(\n";
910 ksort($aliases);
911 foreach ($aliases as $alias => $id) {
912 $id = (string) $id;
913 while (isset($aliases[$id])) {
914 $id = (string) $aliases[$id];
915 }
916 $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n";
917 }
918
919 return $code." );\n";
920 }
921
922 /**
923 * Adds default parameters method.
924 *
925 * @return string
926 */
927 private function addDefaultParametersMethod()
928 {
929 if (!$this->container->getParameterBag()->all()) {
930 return '';
931 }
932
933 $parameters = $this->exportParameters($this->container->getParameterBag()->all());
934
935 $code = '';
936 if ($this->container->isFrozen()) {
937 $code .= <<<'EOF'
938
939 /**
940 * {@inheritdoc}
941 */
942 public function getParameter($name)
943 {
944 $name = strtolower($name);
945
946 if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
947 throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
948 }
949
950 return $this->parameters[$name];
951 }
952
953 /**
954 * {@inheritdoc}
955 */
956 public function hasParameter($name)
957 {
958 $name = strtolower($name);
959
960 return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
961 }
962
963 /**
964 * {@inheritdoc}
965 */
966 public function setParameter($name, $value)
967 {
968 throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
969 }
970
971 /**
972 * {@inheritdoc}
973 */
974 public function getParameterBag()
975 {
976 if (null === $this->parameterBag) {
977 $this->parameterBag = new FrozenParameterBag($this->parameters);
978 }
979
980 return $this->parameterBag;
981 }
982 EOF;
983 if ('' === $this->docStar) {
984 $code = str_replace('/**', '/*', $code);
985 }
986 }
987
988 $code .= <<<EOF
989
990 /*{$this->docStar}
991 * Gets the default parameters.
992 *
993 * @return array An array of the default parameters
994 */
995 protected function getDefaultParameters()
996 {
997 return $parameters;
998 }
999
1000 EOF;
1001
1002 return $code;
1003 }
1004
1005 /**
1006 * Exports parameters.
1007 *
1008 * @param array $parameters
1009 * @param string $path
1010 * @param int $indent
1011 *
1012 * @return string
1013 *
1014 * @throws InvalidArgumentException
1015 */
1016 private function exportParameters($parameters, $path = '', $indent = 12)
1017 {
1018 $php = array();
1019 foreach ($parameters as $key => $value) {
1020 if (is_array($value)) {
1021 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
1022 } elseif ($value instanceof Variable) {
1023 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
1024 } elseif ($value instanceof Definition) {
1025 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));
1026 } elseif ($value instanceof Reference) {
1027 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));
1028 } else {
1029 $value = $this->export($value);
1030 }
1031
1032 $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
1033 }
1034
1035 return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
1036 }
1037
1038 /**
1039 * Ends the class definition.
1040 *
1041 * @return string
1042 */
1043 private function endClass()
1044 {
1045 return <<<'EOF'
1046 }
1047
1048 EOF;
1049 }
1050
1051 /**
1052 * Wraps the service conditionals.
1053 *
1054 * @param string $value
1055 * @param string $code
1056 *
1057 * @return string
1058 */
1059 private function wrapServiceConditionals($value, $code)
1060 {
1061 if (!$services = ContainerBuilder::getServiceConditionals($value)) {
1062 return $code;
1063 }
1064
1065 $conditions = array();
1066 foreach ($services as $service) {
1067 $conditions[] = sprintf("\$this->has('%s')", $service);
1068 }
1069
1070 // re-indent the wrapped code
1071 $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
1072
1073 return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
1074 }
1075
1076 /**
1077 * Builds service calls from arguments.
1078 *
1079 * @param array $arguments
1080 * @param array &$calls By reference
1081 * @param array &$behavior By reference
1082 */
1083 private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
1084 {
1085 foreach ($arguments as $argument) {
1086 if (is_array($argument)) {
1087 $this->getServiceCallsFromArguments($argument, $calls, $behavior);
1088 } elseif ($argument instanceof Reference) {
1089 $id = (string) $argument;
1090
1091 if (!isset($calls[$id])) {
1092 $calls[$id] = 0;
1093 }
1094 if (!isset($behavior[$id])) {
1095 $behavior[$id] = $argument->getInvalidBehavior();
1096 } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
1097 $behavior[$id] = $argument->getInvalidBehavior();
1098 }
1099
1100 ++$calls[$id];
1101 }
1102 }
1103 }
1104
1105 /**
1106 * Returns the inline definition.
1107 *
1108 * @param Definition $definition
1109 *
1110 * @return array
1111 */
1112 private function getInlinedDefinitions(Definition $definition)
1113 {
1114 if (false === $this->inlinedDefinitions->contains($definition)) {
1115 $definitions = array_merge(
1116 $this->getDefinitionsFromArguments($definition->getArguments()),
1117 $this->getDefinitionsFromArguments($definition->getMethodCalls()),
1118 $this->getDefinitionsFromArguments($definition->getProperties())
1119 );
1120
1121 $this->inlinedDefinitions->offsetSet($definition, $definitions);
1122
1123 return $definitions;
1124 }
1125
1126 return $this->inlinedDefinitions->offsetGet($definition);
1127 }
1128
1129 /**
1130 * Gets the definition from arguments.
1131 *
1132 * @param array $arguments
1133 *
1134 * @return array
1135 */
1136 private function getDefinitionsFromArguments(array $arguments)
1137 {
1138 $definitions = array();
1139 foreach ($arguments as $argument) {
1140 if (is_array($argument)) {
1141 $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
1142 } elseif ($argument instanceof Definition) {
1143 $definitions = array_merge(
1144 $definitions,
1145 $this->getInlinedDefinitions($argument),
1146 array($argument)
1147 );
1148 }
1149 }
1150
1151 return $definitions;
1152 }
1153
1154 /**
1155 * Checks if a service id has a reference.
1156 *
1157 * @param string $id
1158 * @param array $arguments
1159 * @param bool $deep
1160 * @param array $visited
1161 *
1162 * @return bool
1163 */
1164 private function hasReference($id, array $arguments, $deep = false, &$visited = array())
1165 {
1166 foreach ($arguments as $argument) {
1167 if (is_array($argument)) {
1168 if ($this->hasReference($id, $argument, $deep, $visited)) {
1169 return true;
1170 }
1171 } elseif ($argument instanceof Reference) {
1172 if ($id === (string) $argument) {
1173 return true;
1174 }
1175
1176 if ($deep && !isset($visited[(string) $argument]) && 'service_container' !== (string) $argument) {
1177 $visited[(string) $argument] = true;
1178
1179 $service = $this->container->getDefinition((string) $argument);
1180 $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
1181
1182 if ($this->hasReference($id, $arguments, $deep, $visited)) {
1183 return true;
1184 }
1185 }
1186 }
1187 }
1188
1189 return false;
1190 }
1191
1192 /**
1193 * Dumps values.
1194 *
1195 * @param array $value
1196 * @param bool $interpolate
1197 *
1198 * @return string
1199 *
1200 * @throws RuntimeException
1201 */
1202 private function dumpValue($value, $interpolate = true)
1203 {
1204 if (is_array($value)) {
1205 $code = array();
1206 foreach ($value as $k => $v) {
1207 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1208 }
1209
1210 return sprintf('array(%s)', implode(', ', $code));
1211 } elseif ($value instanceof Definition) {
1212 if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
1213 return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
1214 }
1215 if (count($value->getMethodCalls()) > 0) {
1216 throw new RuntimeException('Cannot dump definitions which have method calls.');
1217 }
1218 if (null !== $value->getConfigurator()) {
1219 throw new RuntimeException('Cannot dump definitions which have a configurator.');
1220 }
1221
1222 $arguments = array();
1223 foreach ($value->getArguments() as $argument) {
1224 $arguments[] = $this->dumpValue($argument);
1225 }
1226
1227 if (null !== $value->getFactoryMethod()) {
1228 if (null !== $value->getFactoryClass()) {
1229 return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
1230 } elseif (null !== $value->getFactoryService()) {
1231 $service = $this->dumpValue($value->getFactoryService());
1232
1233 return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ? sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments));
1234 } else {
1235 throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
1236 }
1237 }
1238
1239 $class = $value->getClass();
1240 if (null === $class) {
1241 throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
1242 }
1243 $class = $this->dumpValue($class);
1244 if (false !== strpos($class, '$')) {
1245 throw new RuntimeException('Cannot dump definitions which have a variable class name.');
1246 }
1247
1248 return sprintf('new \\%s(%s)', substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
1249 } elseif ($value instanceof Variable) {
1250 return '$'.$value;
1251 } elseif ($value instanceof Reference) {
1252 if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
1253 return $this->dumpValue($this->referenceVariables[$id], $interpolate);
1254 }
1255
1256 return $this->getServiceCall((string) $value, $value);
1257 } elseif ($value instanceof Parameter) {
1258 return $this->dumpParameter($value);
1259 } elseif (true === $interpolate && is_string($value)) {
1260 if (preg_match('/^%([^%]+)%$/', $value, $match)) {
1261 // we do this to deal with non string values (Boolean, integer, ...)
1262 // the preg_replace_callback converts them to strings
1263 return $this->dumpParameter(strtolower($match[1]));
1264 } else {
1265 $that = $this;
1266 $replaceParameters = function ($match) use ($that) {
1267 return "'.".$that->dumpParameter(strtolower($match[2])).".'";
1268 };
1269
1270 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
1271
1272 return $code;
1273 }
1274 } elseif (is_object($value) || is_resource($value)) {
1275 throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
1276 } else {
1277 return $this->export($value);
1278 }
1279 }
1280
1281 /**
1282 * Dumps a parameter.
1283 *
1284 * @param string $name
1285 *
1286 * @return string
1287 */
1288 public function dumpParameter($name)
1289 {
1290 if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
1291 return $this->dumpValue($this->container->getParameter($name), false);
1292 }
1293
1294 return sprintf("\$this->getParameter('%s')", strtolower($name));
1295 }
1296
1297 /**
1298 * Gets a service call.
1299 *
1300 * @param string $id
1301 * @param Reference $reference
1302 *
1303 * @return string
1304 */
1305 private function getServiceCall($id, Reference $reference = null)
1306 {
1307 if ('service_container' === $id) {
1308 return '$this';
1309 }
1310
1311 if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
1312 return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
1313 } else {
1314 if ($this->container->hasAlias($id)) {
1315 $id = (string) $this->container->getAlias($id);
1316 }
1317
1318 return sprintf('$this->get(\'%s\')', $id);
1319 }
1320 }
1321
1322 /**
1323 * Convert a service id to a valid PHP method name.
1324 *
1325 * @param string $id
1326 *
1327 * @return string
1328 *
1329 * @throws InvalidArgumentException
1330 */
1331 private function camelize($id)
1332 {
1333 $name = Container::camelize($id);
1334
1335 if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
1336 throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
1337 }
1338
1339 return $name;
1340 }
1341
1342 /**
1343 * Returns the next name to use.
1344 *
1345 * @return string
1346 */
1347 private function getNextVariableName()
1348 {
1349 $firstChars = self::FIRST_CHARS;
1350 $firstCharsLength = strlen($firstChars);
1351 $nonFirstChars = self::NON_FIRST_CHARS;
1352 $nonFirstCharsLength = strlen($nonFirstChars);
1353
1354 while (true) {
1355 $name = '';
1356 $i = $this->variableCount;
1357
1358 if ('' === $name) {
1359 $name .= $firstChars[$i % $firstCharsLength];
1360 $i = (int) ($i / $firstCharsLength);
1361 }
1362
1363 while ($i > 0) {
1364 --$i;
1365 $name .= $nonFirstChars[$i % $nonFirstCharsLength];
1366 $i = (int) ($i / $nonFirstCharsLength);
1367 }
1368
1369 ++$this->variableCount;
1370
1371 // check that the name is not reserved
1372 if (in_array($name, $this->reservedVariables, true)) {
1373 continue;
1374 }
1375
1376 return $name;
1377 }
1378 }
1379
1380 private function exportTargetDirs()
1381 {
1382 return null === $this->targetDirRegex ? '' : <<<EOF
1383
1384 \$dir = __DIR__;
1385 for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
1386 \$this->targetDirs[\$i] = \$dir = dirname(\$dir);
1387 }
1388 EOF;
1389 }
1390
1391 private function export($value)
1392 {
1393 if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {
1394 $prefix = $matches[0][1] ? var_export(substr($value, 0, $matches[0][1]), true).'.' : '';
1395 $suffix = $matches[0][1] + strlen($matches[0][0]);
1396 $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : '';
1397 $dirname = '__DIR__';
1398
1399 if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) {
1400 $dirname = sprintf('$this->targetDirs[%d]', $offset);
1401 }
1402
1403 if ($prefix || $suffix) {
1404 return sprintf('(%s%s%s)', $prefix, $dirname, $suffix);
1405 }
1406
1407 return $dirname;
1408 }
1409
1410 return var_export($value, true);
1411 }
1412 }