4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\DependencyInjection\Dumper
;
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
;
29 * PhpDumper dumps a service container as a PHP class.
31 * @author Fabien Potencier <fabien@symfony.com>
32 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
34 class PhpDumper
extends Dumper
37 * Characters that might appear in the generated variable name as first character.
41 const FIRST_CHARS
= 'abcdefghijklmnopqrstuvwxyz';
44 * Characters that might appear in the generated variable name as any but the first character.
48 const NON_FIRST_CHARS
= 'abcdefghijklmnopqrstuvwxyz0123456789_';
50 private $inlinedDefinitions;
51 private $definitionVariables;
52 private $referenceVariables;
53 private $variableCount;
54 private $reservedVariables = array('instance', 'class');
55 private $targetDirRegex;
56 private $targetDirMaxMatches;
60 * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
67 public function __construct(ContainerBuilder
$container)
69 parent
::__construct($container);
71 $this->inlinedDefinitions
= new \
SplObjectStorage();
75 * Sets the dumper to be used when dumping proxies in the generated container.
77 * @param ProxyDumper $proxyDumper
79 public function setProxyDumper(ProxyDumper
$proxyDumper)
81 $this->proxyDumper
= $proxyDumper;
85 * Dumps the service container as a PHP class.
89 * * class: The class name
90 * * base_class: The base class name
92 * @param array $options An array of options
94 * @return string A PHP class representing of the service container
96 public function dump(array $options = array())
98 $this->targetDirRegex
= null;
99 $options = array_merge(array(
100 'class' => 'ProjectServiceContainer',
101 'base_class' => 'Container',
104 $this->docStar
= $options['debug'] ?
'*' : '';
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.
111 $dir = explode(DIRECTORY_SEPARATOR
, realpath($dir));
116 $lastOptionalDir = $i > 8 ?
$i - 5 : 3;
117 $this->targetDirMaxMatches
= $i - $lastOptionalDir;
119 while (--$i >= $lastOptionalDir) {
120 $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR
.$dir[$i], '#'), $regex);
124 $regex = preg_quote(DIRECTORY_SEPARATOR
.$dir[$i], '#').$regex;
127 $this->targetDirRegex
= '#'.preg_quote($dir[0], '#').$regex.'#';
131 $code = $this->startClass($options['class'], $options['base_class']);
133 if ($this->container
->isFrozen()) {
134 $code .= $this->addFrozenConstructor();
136 $code .= $this->addConstructor();
140 $this->addServices().
141 $this->addDefaultParametersMethod().
143 $this->addProxyClasses()
145 $this->targetDirRegex
= null;
151 * Retrieves the currently set proxy dumper or instantiates one.
153 * @return ProxyDumper
155 private function getProxyDumper()
157 if (!$this->proxyDumper
) {
158 $this->proxyDumper
= new NullDumper();
161 return $this->proxyDumper
;
165 * Generates Service local temp variables.
168 * @param string $definition
172 private function addServiceLocalTempVariables($cId, $definition)
174 static $template = " \$%s = %s;\n";
176 $localDefinitions = array_merge(
178 $this->getInlinedDefinitions($definition)
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);
189 foreach ($calls as $id => $callCount) {
190 if ('service_container' === $id ||
$id === $cId) {
194 if ($callCount > 1) {
195 $name = $this->getNextVariableName();
196 $this->referenceVariables
[$id] = new Variable($name);
198 if (ContainerInterface
::EXCEPTION_ON_INVALID_REFERENCE
=== $behavior[$id]) {
199 $code .= sprintf($template, $name, $this->getServiceCall($id));
201 $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface
::NULL_ON_INVALID_REFERENCE
)));
214 * Generates code for the proxies to be attached after the container class.
218 private function addProxyClasses()
220 /* @var $definitions Definition[] */
221 $definitions = array_filter(
222 $this->container
->getDefinitions(),
223 array($this->getProxyDumper(), 'isProxyCandidate')
226 $strip = '' === $this->docStar
&& method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
228 foreach ($definitions as $definition) {
229 $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition);
231 $proxyCode = "<?php\n".$proxyCode;
232 $proxyCode = substr(Kernel
::stripComments($proxyCode), 5);
241 * Generates the require_once statement for service includes.
243 * @param string $id The service id
244 * @param Definition $definition
248 private function addServiceInclude($id, $definition)
250 $template = " require_once %s;\n";
253 if (null !== $file = $definition->getFile()) {
254 $code .= sprintf($template, $this->dumpValue($file));
257 foreach ($this->getInlinedDefinitions($definition) as $definition) {
258 if (null !== $file = $definition->getFile()) {
259 $code .= sprintf($template, $this->dumpValue($file));
271 * Generates the inline definition of a service.
274 * @param Definition $definition
278 * @throws RuntimeException When the factory definition is incomplete
279 * @throws ServiceCircularReferenceException When a circular reference is detected
281 private function addServiceInlinedDefinitions($id, $definition)
284 $variableMap = $this->definitionVariables
;
285 $nbOccurrences = new \
SplObjectStorage();
286 $processed = new \
SplObjectStorage();
287 $inlinedDefinitions = $this->getInlinedDefinitions($definition);
289 foreach ($inlinedDefinitions as $definition) {
290 if (false === $nbOccurrences->contains($definition)) {
291 $nbOccurrences->offsetSet($definition, 1);
293 $i = $nbOccurrences->offsetGet($definition);
294 $nbOccurrences->offsetSet($definition, $i +
1);
298 foreach ($inlinedDefinitions as $sDefinition) {
299 if ($processed->contains($sDefinition)) {
302 $processed->offsetSet($sDefinition);
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));
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));
320 $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
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);
336 * Adds the service return statement.
338 * @param string $id Service id
339 * @param Definition $definition
343 private function addServiceReturn($id, $definition)
345 if ($this->isSimpleInstance($id, $definition)) {
349 return "\n return \$instance;\n }\n";
353 * Generates the service instance.
356 * @param Definition $definition
360 * @throws InvalidArgumentException
361 * @throws RuntimeException
363 private function addServiceInstance($id, $definition)
365 $class = $definition->getClass();
367 if ('\\' === substr($class, 0, 1)) {
368 $class = substr($class, 1);
371 $class = $this->dumpValue($class);
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));
377 $simple = $this->isSimpleInstance($id, $definition);
378 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
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';
393 $instantiation .= ' = ';
396 $code = $this->addNewInstance($id, $definition, $return, $instantiation);
406 * Checks if the definition is a simple instance.
409 * @param Definition $definition
413 private function isSimpleInstance($id, $definition)
415 foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
416 if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
420 if ($sDefinition->getMethodCalls() ||
$sDefinition->getProperties() ||
$sDefinition->getConfigurator()) {
429 * Adds method calls to a service definition.
432 * @param Definition $definition
433 * @param string $variableName
437 private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
440 foreach ($definition->getMethodCalls() as $call) {
441 $arguments = array();
442 foreach ($call[1] as $value) {
443 $arguments[] = $this->dumpValue($value);
446 $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
452 private function addServiceProperties($id, $definition, $variableName = 'instance')
455 foreach ($definition->getProperties() as $name => $value) {
456 $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
463 * Generates the inline definition setup.
466 * @param Definition $definition
470 * @throws ServiceCircularReferenceException when the container contains a circular reference
472 private function addServiceInlinedDefinitionsSetup($id, $definition)
474 $this->referenceVariables
[$id] = new Variable('instance');
477 $processed = new \
SplObjectStorage();
478 foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
479 if ($processed->contains($iDefinition)) {
482 $processed->offsetSet($iDefinition);
484 if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
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));
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);
508 * Adds configurator definition.
511 * @param Definition $definition
512 * @param string $variableName
516 private function addServiceConfigurator($id, $definition, $variableName = 'instance')
518 if (!$callable = $definition->getConfigurator()) {
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);
527 return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
530 return sprintf(" %s(\$%s);\n", $callable, $variableName);
537 * @param Definition $definition
541 private function addService($id, $definition)
543 $this->definitionVariables
= new \
SplObjectStorage();
544 $this->referenceVariables
= array();
545 $this->variableCount
= 0;
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());
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')) {
564 $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
567 $return = implode("\n * ", $return);
570 if (ContainerInterface
::SCOPE_PROTOTYPE
!== $scope) {
574 * This service is shared.
575 * This method always returns the same instance of the service.
579 if (!$definition->isPublic()) {
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.
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 *";
593 $lazyInitialization = '';
594 $lazyInitializationDoc = '';
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';
603 * Gets the '$id' service.$doc
604 *$lazyInitializationDoc
607 {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
612 $code .= $isProxyCandidate ?
$this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
614 if (!in_array($scope, array(ContainerInterface
::SCOPE_CONTAINER
, ContainerInterface
::SCOPE_PROTOTYPE
))) {
616 if (!isset(\$this->scopedServices['$scope'])) {
617 throw new InactiveScopeException('$id', '$scope');
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);
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)
640 $this->definitionVariables
= null;
641 $this->referenceVariables
= null;
647 * Adds multiple services.
651 private function addServices()
653 $publicServices = $privateServices = $synchronizers = '';
654 $definitions = $this->container
->getDefinitions();
656 foreach ($definitions as $id => $definition) {
657 if ($definition->isPublic()) {
658 $publicServices .= $this->addService($id, $definition);
660 $privateServices .= $this->addService($id, $definition);
663 $synchronizers .= $this->addServiceSynchronizer($id, $definition);
666 return $publicServices.$synchronizers.$privateServices;
670 * Adds synchronizer methods.
672 * @param string $id A service identifier
673 * @param Definition $definition A Definition instance
675 * @return string|null
677 private function addServiceSynchronizer($id, Definition
$definition)
679 if (!$definition->isSynchronized()) {
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);
693 $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
696 if (\$this->initialized('$definitionId')) {
713 * Updates the '$id' service.
715 protected function synchronize{$this->camelize($id)}Service()
722 private function addNewInstance($id, Definition
$definition, $return, $instantiation)
724 $class = $this->dumpValue($definition->getClass());
726 $arguments = array();
727 foreach ($definition->getArguments() as $value) {
728 $arguments[] = $this->dumpValue($value);
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) : '');
736 if (null !== $definition->getFactoryService()) {
737 return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
740 throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
743 if (false !== strpos($class, '$')) {
744 return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
747 return sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
751 * Adds the class headers.
753 * @param string $class Class name
754 * @param string $baseClass The name of the base class
758 private function startClass($class, $baseClass)
760 $bagClass = $this->container
->isFrozen() ?
'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
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;
776 * This class has been auto-generated
777 * by the Symfony Dependency Injection Component.
779 class $class extends $baseClass
781 private \$parameters;
782 private \$targetDirs = array();
788 * Adds the constructor.
792 private function addConstructor()
794 $targetDirs = $this->exportTargetDirs();
795 $arguments = $this->container
->getParameterBag()->all() ?
'new ParameterBag($this->getDefaultParameters())' : null;
802 public function __construct()
804 parent::__construct($arguments);
808 if (count($scopes = $this->container
->getScopes()) > 0) {
810 $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
811 $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container
->getScopeChildren()).";\n";
814 $code .= $this->addMethodMap();
815 $code .= $this->addAliases();
826 * Adds the constructor for a frozen container.
830 private function addFrozenConstructor()
832 $targetDirs = $this->exportTargetDirs();
839 public function __construct()
843 if ($this->container
->getParameterBag()->all()) {
844 $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
850 $this->scopedServices =
851 $this->scopeStacks = array();
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";
859 $code .= " \$this->scopes = array();\n";
860 $code .= " \$this->scopeChildren = array();\n";
863 $code .= $this->addMethodMap();
864 $code .= $this->addAliases();
875 * Adds the methodMap property definition.
879 private function addMethodMap()
881 if (!$definitions = $this->container
->getDefinitions()) {
885 $code = " \$this->methodMap = array(\n";
887 foreach ($definitions as $id => $definition) {
888 $code .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
891 return $code." );\n";
895 * Adds the aliases property definition.
899 private function addAliases()
901 if (!$aliases = $this->container
->getAliases()) {
902 if ($this->container
->isFrozen()) {
903 return "\n \$this->aliases = array();\n";
909 $code = " \$this->aliases = array(\n";
911 foreach ($aliases as $alias => $id) {
913 while (isset($aliases[$id])) {
914 $id = (string) $aliases[$id];
916 $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n";
919 return $code." );\n";
923 * Adds default parameters method.
927 private function addDefaultParametersMethod()
929 if (!$this->container
->getParameterBag()->all()) {
933 $parameters = $this->exportParameters($this->container
->getParameterBag()->all());
936 if ($this->container
->isFrozen()) {
942 public function getParameter($name)
944 $name = strtolower($name);
946 if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
947 throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
950 return $this->parameters[$name];
956 public function hasParameter($name)
958 $name = strtolower($name);
960 return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
966 public function setParameter($name, $value)
968 throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
974 public function getParameterBag()
976 if (null === $this->parameterBag) {
977 $this->parameterBag = new FrozenParameterBag($this->parameters);
980 return $this->parameterBag;
983 if ('' === $this->docStar
) {
984 $code = str_replace('/**', '/*', $code);
991 * Gets the default parameters.
993 * @return array An array of the default parameters
995 protected function getDefaultParameters()
1006 * Exports parameters.
1008 * @param array $parameters
1009 * @param string $path
1010 * @param int $indent
1014 * @throws InvalidArgumentException
1016 private function exportParameters($parameters, $path = '', $indent = 12)
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));
1029 $value = $this->export($value);
1032 $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
1035 return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
1039 * Ends the class definition.
1043 private function endClass()
1052 * Wraps the service conditionals.
1054 * @param string $value
1055 * @param string $code
1059 private function wrapServiceConditionals($value, $code)
1061 if (!$services = ContainerBuilder
::getServiceConditionals($value)) {
1065 $conditions = array();
1066 foreach ($services as $service) {
1067 $conditions[] = sprintf("\$this->has('%s')", $service);
1070 // re-indent the wrapped code
1071 $code = implode("\n", array_map(function ($line) { return $line ?
' '.$line : $line; }, explode("\n", $code)));
1073 return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
1077 * Builds service calls from arguments.
1079 * @param array $arguments
1080 * @param array &$calls By reference
1081 * @param array &$behavior By reference
1083 private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
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;
1091 if (!isset($calls[$id])) {
1094 if (!isset($behavior[$id])) {
1095 $behavior[$id] = $argument->getInvalidBehavior();
1096 } elseif (ContainerInterface
::EXCEPTION_ON_INVALID_REFERENCE
!== $behavior[$id]) {
1097 $behavior[$id] = $argument->getInvalidBehavior();
1106 * Returns the inline definition.
1108 * @param Definition $definition
1112 private function getInlinedDefinitions(Definition
$definition)
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())
1121 $this->inlinedDefinitions
->offsetSet($definition, $definitions);
1123 return $definitions;
1126 return $this->inlinedDefinitions
->offsetGet($definition);
1130 * Gets the definition from arguments.
1132 * @param array $arguments
1136 private function getDefinitionsFromArguments(array $arguments)
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(
1145 $this->getInlinedDefinitions($argument),
1151 return $definitions;
1155 * Checks if a service id has a reference.
1158 * @param array $arguments
1160 * @param array $visited
1164 private function hasReference($id, array $arguments, $deep = false, &$visited = array())
1166 foreach ($arguments as $argument) {
1167 if (is_array($argument)) {
1168 if ($this->hasReference($id, $argument, $deep, $visited)) {
1171 } elseif ($argument instanceof Reference
) {
1172 if ($id === (string) $argument) {
1176 if ($deep && !isset($visited[(string) $argument]) && 'service_container' !== (string) $argument) {
1177 $visited[(string) $argument] = true;
1179 $service = $this->container
->getDefinition((string) $argument);
1180 $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
1182 if ($this->hasReference($id, $arguments, $deep, $visited)) {
1195 * @param array $value
1196 * @param bool $interpolate
1200 * @throws RuntimeException
1202 private function dumpValue($value, $interpolate = true)
1204 if (is_array($value)) {
1206 foreach ($value as $k => $v) {
1207 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
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);
1215 if (count($value->getMethodCalls()) > 0) {
1216 throw new RuntimeException('Cannot dump definitions which have method calls.');
1218 if (null !== $value->getConfigurator()) {
1219 throw new RuntimeException('Cannot dump definitions which have a configurator.');
1222 $arguments = array();
1223 foreach ($value->getArguments() as $argument) {
1224 $arguments[] = $this->dumpValue($argument);
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());
1233 return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ?
sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments));
1235 throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
1239 $class = $value->getClass();
1240 if (null === $class) {
1241 throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
1243 $class = $this->dumpValue($class);
1244 if (false !== strpos($class, '$')) {
1245 throw new RuntimeException('Cannot dump definitions which have a variable class name.');
1248 return sprintf('new \\%s(%s)', substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
1249 } elseif ($value instanceof Variable
) {
1251 } elseif ($value instanceof Reference
) {
1252 if (null !== $this->referenceVariables
&& isset($this->referenceVariables
[$id = (string) $value])) {
1253 return $this->dumpValue($this->referenceVariables
[$id], $interpolate);
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]));
1266 $replaceParameters = function ($match) use ($that) {
1267 return "'.".$that->dumpParameter(strtolower($match[2])).".'";
1270 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
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.');
1277 return $this->export($value);
1282 * Dumps a parameter.
1284 * @param string $name
1288 public function dumpParameter($name)
1290 if ($this->container
->isFrozen() && $this->container
->hasParameter($name)) {
1291 return $this->dumpValue($this->container
->getParameter($name), false);
1294 return sprintf("\$this->getParameter('%s')", strtolower($name));
1298 * Gets a service call.
1301 * @param Reference $reference
1305 private function getServiceCall($id, Reference
$reference = null)
1307 if ('service_container' === $id) {
1311 if (null !== $reference && ContainerInterface
::EXCEPTION_ON_INVALID_REFERENCE
!== $reference->getInvalidBehavior()) {
1312 return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
1314 if ($this->container
->hasAlias($id)) {
1315 $id = (string) $this->container
->getAlias($id);
1318 return sprintf('$this->get(\'%s\')', $id);
1323 * Convert a service id to a valid PHP method name.
1329 * @throws InvalidArgumentException
1331 private function camelize($id)
1333 $name = Container
::camelize($id);
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));
1343 * Returns the next name to use.
1347 private function getNextVariableName()
1349 $firstChars = self
::FIRST_CHARS
;
1350 $firstCharsLength = strlen($firstChars);
1351 $nonFirstChars = self
::NON_FIRST_CHARS
;
1352 $nonFirstCharsLength = strlen($nonFirstChars);
1356 $i = $this->variableCount
;
1359 $name .= $firstChars[$i %
$firstCharsLength];
1360 $i = (int) ($i / $firstCharsLength);
1365 $name .= $nonFirstChars[$i %
$nonFirstCharsLength];
1366 $i = (int) ($i / $nonFirstCharsLength);
1369 ++
$this->variableCount
;
1371 // check that the name is not reserved
1372 if (in_array($name, $this->reservedVariables
, true)) {
1380 private function exportTargetDirs()
1382 return null === $this->targetDirRegex ?
'' : <<<EOF
1385 for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
1386 \$this->targetDirs[\$i] = \$dir = dirname(\$dir);
1391 private function export($value)
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__';
1399 if (0 < $offset = 1 +
$this->targetDirMaxMatches
- count($matches)) {
1400 $dirname = sprintf('$this->targetDirs[%d]', $offset);
1403 if ($prefix ||
$suffix) {
1404 return sprintf('(%s%s%s)', $prefix, $dirname, $suffix);
1410 return var_export($value, true);