vendor/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     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\Exception\ServiceCircularReferenceException;
       
    15 use Symfony\Component\DependencyInjection\Variable;
       
    16 use Symfony\Component\DependencyInjection\Definition;
       
    17 use Symfony\Component\DependencyInjection\ContainerBuilder;
       
    18 use Symfony\Component\DependencyInjection\Container;
       
    19 use Symfony\Component\DependencyInjection\ContainerInterface;
       
    20 use Symfony\Component\DependencyInjection\Reference;
       
    21 use Symfony\Component\DependencyInjection\Parameter;
       
    22 
       
    23 /**
       
    24  * PhpDumper dumps a service container as a PHP class.
       
    25  *
       
    26  * @author Fabien Potencier <fabien@symfony.com>
       
    27  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
       
    28  *
       
    29  * @api
       
    30  */
       
    31 class PhpDumper extends Dumper
       
    32 {
       
    33     /**
       
    34      * Characters that might appear in the generated variable name as first character
       
    35      * @var string
       
    36      */
       
    37     const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
       
    38 
       
    39     /**
       
    40      * Characters that might appear in the generated variable name as any but the first character
       
    41      * @var string
       
    42      */
       
    43     const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
       
    44 
       
    45     private $inlinedDefinitions;
       
    46     private $definitionVariables;
       
    47     private $referenceVariables;
       
    48     private $variableCount;
       
    49     private $reservedVariables = array('instance', 'class');
       
    50 
       
    51     /**
       
    52      * {@inheritDoc}
       
    53      *
       
    54      * @api
       
    55      */
       
    56     public function __construct(ContainerBuilder $container)
       
    57     {
       
    58         parent::__construct($container);
       
    59 
       
    60         $this->inlinedDefinitions = new \SplObjectStorage;
       
    61     }
       
    62 
       
    63     /**
       
    64      * Dumps the service container as a PHP class.
       
    65      *
       
    66      * Available options:
       
    67      *
       
    68      *  * class:      The class name
       
    69      *  * base_class: The base class name
       
    70      *
       
    71      * @param  array  $options An array of options
       
    72      *
       
    73      * @return string A PHP class representing of the service container
       
    74      *
       
    75      * @api
       
    76      */
       
    77     public function dump(array $options = array())
       
    78     {
       
    79         $options = array_merge(array(
       
    80             'class'      => 'ProjectServiceContainer',
       
    81             'base_class' => 'Container',
       
    82         ), $options);
       
    83 
       
    84         $code = $this->startClass($options['class'], $options['base_class']);
       
    85 
       
    86         if ($this->container->isFrozen()) {
       
    87             $code .= $this->addFrozenConstructor();
       
    88         } else {
       
    89             $code .= $this->addConstructor();
       
    90         }
       
    91 
       
    92         $code .=
       
    93             $this->addServices().
       
    94             $this->addDefaultParametersMethod().
       
    95             $this->endClass()
       
    96         ;
       
    97 
       
    98         return $code;
       
    99     }
       
   100 
       
   101     /**
       
   102      * Generates Service local temp variables.
       
   103      *
       
   104      * @param string $cId
       
   105      * @param string $definition
       
   106      * @return string
       
   107      */
       
   108     private function addServiceLocalTempVariables($cId, $definition)
       
   109     {
       
   110         static $template = "        \$%s = %s;\n";
       
   111 
       
   112         $localDefinitions = array_merge(
       
   113             array($definition),
       
   114             $this->getInlinedDefinitions($definition)
       
   115         );
       
   116 
       
   117         $calls = $behavior = array();
       
   118         foreach ($localDefinitions as $iDefinition) {
       
   119             $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
       
   120             $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
       
   121             $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
       
   122         }
       
   123 
       
   124         $code = '';
       
   125         foreach ($calls as $id => $callCount) {
       
   126             if ('service_container' === $id || $id === $cId) {
       
   127                 continue;
       
   128             }
       
   129 
       
   130             if ($callCount > 1) {
       
   131                 $name = $this->getNextVariableName();
       
   132                 $this->referenceVariables[$id] = new Variable($name);
       
   133 
       
   134                 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
       
   135                     $code .= sprintf($template, $name, $this->getServiceCall($id));
       
   136                 } else {
       
   137                     $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
       
   138                 }
       
   139             }
       
   140         }
       
   141 
       
   142         if ('' !== $code) {
       
   143             $code .= "\n";
       
   144         }
       
   145 
       
   146         return $code;
       
   147     }
       
   148 
       
   149     /**
       
   150      * Generates the require_once statement for service includes.
       
   151      *
       
   152      * @param string $id The service id
       
   153      * @param Definition $definition
       
   154      * @return string
       
   155      */
       
   156     private function addServiceInclude($id, $definition)
       
   157     {
       
   158         $template = "        require_once %s;\n";
       
   159         $code = '';
       
   160 
       
   161         if (null !== $file = $definition->getFile()) {
       
   162             $code .= sprintf($template, $this->dumpValue($file));
       
   163         }
       
   164 
       
   165         foreach ($this->getInlinedDefinitions($definition) as $definition) {
       
   166             if (null !== $file = $definition->getFile()) {
       
   167                 $code .= sprintf($template, $this->dumpValue($file));
       
   168             }
       
   169         }
       
   170 
       
   171         if ('' !== $code) {
       
   172             $code .= "\n";
       
   173         }
       
   174 
       
   175         return $code;
       
   176     }
       
   177 
       
   178     /**
       
   179      * Generates the inline definition of a service.
       
   180      *
       
   181      * @param string $id
       
   182      * @param Definition $definition
       
   183      * @return string
       
   184      */
       
   185     private function addServiceInlinedDefinitions($id, $definition)
       
   186     {
       
   187         $code = '';
       
   188         $variableMap = $this->definitionVariables;
       
   189         $nbOccurrences = new \SplObjectStorage();
       
   190         $processed = new \SplObjectStorage();
       
   191         $inlinedDefinitions = $this->getInlinedDefinitions($definition);
       
   192 
       
   193         foreach ($inlinedDefinitions as $definition) {
       
   194             if (false === $nbOccurrences->contains($definition)) {
       
   195                 $nbOccurrences->offsetSet($definition, 1);
       
   196             } else {
       
   197                 $i = $nbOccurrences->offsetGet($definition);
       
   198                 $nbOccurrences->offsetSet($definition, $i+1);
       
   199             }
       
   200         }
       
   201 
       
   202         foreach ($inlinedDefinitions as $sDefinition) {
       
   203             if ($processed->contains($sDefinition)) {
       
   204                 continue;
       
   205             }
       
   206             $processed->offsetSet($sDefinition);
       
   207 
       
   208             $class = $this->dumpValue($sDefinition->getClass());
       
   209             if ($nbOccurrences->offsetGet($sDefinition) > 1 || count($sDefinition->getMethodCalls()) > 0 || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
       
   210                 $name = $this->getNextVariableName();
       
   211                 $variableMap->offsetSet($sDefinition, new Variable($name));
       
   212 
       
   213                 // a construct like:
       
   214                 // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
       
   215                 // this is an indication for a wrong implementation, you can circumvent this problem
       
   216                 // by setting up your service structure like this:
       
   217                 // $b = new ServiceB();
       
   218                 // $a = new ServiceA(ServiceB $b);
       
   219                 // $b->setServiceA(ServiceA $a);
       
   220                 if ($this->hasReference($id, $sDefinition->getArguments())) {
       
   221                     throw new ServiceCircularReferenceException($id, array($id));
       
   222                 }
       
   223 
       
   224                 $arguments = array();
       
   225                 foreach ($sDefinition->getArguments() as $argument) {
       
   226                     $arguments[] = $this->dumpValue($argument);
       
   227                 }
       
   228 
       
   229                 if (null !== $sDefinition->getFactoryMethod()) {
       
   230                     if (null !== $sDefinition->getFactoryClass()) {
       
   231                         $code .= sprintf("        \$%s = call_user_func(array(%s, '%s')%s);\n", $name, $this->dumpValue($sDefinition->getFactoryClass()), $sDefinition->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
       
   232                     } elseif (null !== $sDefinition->getFactoryService()) {
       
   233                         $code .= sprintf("        \$%s = %s->%s(%s);\n", $name, $this->getServiceCall($sDefinition->getFactoryService()), $sDefinition->getFactoryMethod(), implode(', ', $arguments));
       
   234                     } else {
       
   235                         throw new \RuntimeException('Factory service or factory class must be defined in service definition for '.$id);
       
   236                     }
       
   237                 } elseif (false !== strpos($class, '$')) {
       
   238                     $code .= sprintf("        \$class = %s;\n        \$%s = new \$class(%s);\n", $class, $name, implode(', ', $arguments));
       
   239                 } else {
       
   240                     $code .= sprintf("        \$%s = new \\%s(%s);\n", $name, substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
       
   241                 }
       
   242 
       
   243                 if (!$this->hasReference($id, $sDefinition->getMethodCalls()) && !$this->hasReference($id, $sDefinition->getProperties())) {
       
   244                     $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
       
   245                     $code .= $this->addServiceProperties(null, $sDefinition, $name);
       
   246                     $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
       
   247                 }
       
   248 
       
   249                 $code .= "\n";
       
   250             }
       
   251         }
       
   252 
       
   253         return $code;
       
   254     }
       
   255 
       
   256     /**
       
   257      * Adds the service return statement.
       
   258      *
       
   259      * @param string $id Service id
       
   260      * @param Definition $definition
       
   261      * @return string
       
   262      */
       
   263     private function addServiceReturn($id, $definition)
       
   264     {
       
   265         if ($this->isSimpleInstance($id, $definition)) {
       
   266             return "    }\n";
       
   267         }
       
   268 
       
   269         return "\n        return \$instance;\n    }\n";
       
   270     }
       
   271 
       
   272     /**
       
   273      * Generates the service instance.
       
   274      *
       
   275      * @param string $id
       
   276      * @param Definition $definition
       
   277      * @return string
       
   278      *
       
   279      * @throws \InvalidArgumentException
       
   280      * @throws \RuntimeException
       
   281      */
       
   282     private function addServiceInstance($id, $definition)
       
   283     {
       
   284         $class = $this->dumpValue($definition->getClass());
       
   285 
       
   286         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)) {
       
   287             throw new \InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
       
   288         }
       
   289 
       
   290         $arguments = array();
       
   291         foreach ($definition->getArguments() as $value) {
       
   292             $arguments[] = $this->dumpValue($value);
       
   293         }
       
   294 
       
   295         $simple = $this->isSimpleInstance($id, $definition);
       
   296 
       
   297         $instantiation = '';
       
   298         if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
       
   299             $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
       
   300         } else if (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
       
   301             $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
       
   302         } elseif (!$simple) {
       
   303             $instantiation = '$instance';
       
   304         }
       
   305 
       
   306         $return = '';
       
   307         if ($simple) {
       
   308             $return = 'return ';
       
   309         } else {
       
   310             $instantiation .= ' = ';
       
   311         }
       
   312 
       
   313         if (null !== $definition->getFactoryMethod()) {
       
   314             if (null !== $definition->getFactoryClass()) {
       
   315                 $code = sprintf("        $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
       
   316             } elseif (null !== $definition->getFactoryService()) {
       
   317                 $code = sprintf("        $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
       
   318             } else {
       
   319                 throw new \RuntimeException('Factory method requires a factory service or factory class in service definition for '.$id);
       
   320             }
       
   321         } elseif (false !== strpos($class, '$')) {
       
   322             $code = sprintf("        \$class = %s;\n        $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
       
   323         } else {
       
   324             $code = sprintf("        $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
       
   325         }
       
   326 
       
   327         if (!$simple) {
       
   328             $code .= "\n";
       
   329         }
       
   330 
       
   331         return $code;
       
   332     }
       
   333 
       
   334     /**
       
   335      * Checks if the definition is a simple instance.
       
   336      *
       
   337      * @param string $id
       
   338      * @param Definition $definition
       
   339      * @return Boolean
       
   340      */
       
   341     private function isSimpleInstance($id, $definition)
       
   342     {
       
   343         foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
       
   344             if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
       
   345                 continue;
       
   346             }
       
   347 
       
   348             if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
       
   349                 return false;
       
   350             }
       
   351         }
       
   352 
       
   353         return true;
       
   354     }
       
   355 
       
   356     /**
       
   357      * Adds method calls to a service definition.
       
   358      *
       
   359      * @param string $id
       
   360      * @param Definition $definition
       
   361      * @param string $variableName
       
   362      * @return string
       
   363      */
       
   364     private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
       
   365     {
       
   366         $calls = '';
       
   367         foreach ($definition->getMethodCalls() as $call) {
       
   368             $arguments = array();
       
   369             foreach ($call[1] as $value) {
       
   370                 $arguments[] = $this->dumpValue($value);
       
   371             }
       
   372 
       
   373             $calls .= $this->wrapServiceConditionals($call[1], sprintf("        \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
       
   374         }
       
   375 
       
   376         return $calls;
       
   377     }
       
   378 
       
   379     private function addServiceProperties($id, $definition, $variableName = 'instance')
       
   380     {
       
   381         $code = '';
       
   382         foreach ($definition->getProperties() as $name => $value) {
       
   383             $code .= sprintf("        \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
       
   384         }
       
   385 
       
   386         return $code;
       
   387     }
       
   388 
       
   389     /**
       
   390      * Generates the inline definition setup.
       
   391      *
       
   392      * @param string $id
       
   393      * @param Definition $definition
       
   394      * @return string
       
   395      */
       
   396     private function addServiceInlinedDefinitionsSetup($id, $definition)
       
   397     {
       
   398         $this->referenceVariables[$id] = new Variable('instance');
       
   399 
       
   400         $code = '';
       
   401         $processed = new \SplObjectStorage();
       
   402         foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
       
   403             if ($processed->contains($iDefinition)) {
       
   404                 continue;
       
   405             }
       
   406             $processed->offsetSet($iDefinition);
       
   407 
       
   408             if (!$this->hasReference($id, $iDefinition->getMethodCalls())) {
       
   409                 continue;
       
   410             }
       
   411 
       
   412             if ($iDefinition->getMethodCalls()) {
       
   413                 $code .= $this->addServiceMethodCalls(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition));
       
   414             }
       
   415             if ($iDefinition->getConfigurator()) {
       
   416                 $code .= $this->addServiceConfigurator(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition));
       
   417             }
       
   418         }
       
   419 
       
   420         if ('' !== $code) {
       
   421             $code .= "\n";
       
   422         }
       
   423 
       
   424         return $code;
       
   425     }
       
   426 
       
   427     /**
       
   428      * Adds configurator definition
       
   429      *
       
   430      * @param string $id
       
   431      * @param Definition $definition
       
   432      * @param string $variableName
       
   433      * @return string
       
   434      */
       
   435     private function addServiceConfigurator($id, $definition, $variableName = 'instance')
       
   436     {
       
   437         if (!$callable = $definition->getConfigurator()) {
       
   438             return '';
       
   439         }
       
   440 
       
   441         if (is_array($callable)) {
       
   442             if (is_object($callable[0]) && $callable[0] instanceof Reference) {
       
   443                 return sprintf("        %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $callable[1], $variableName);
       
   444             }
       
   445 
       
   446             return sprintf("        call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
       
   447         }
       
   448 
       
   449         return sprintf("        %s(\$%s);\n", $callable, $variableName);
       
   450     }
       
   451 
       
   452     /**
       
   453      * Adds a service
       
   454      *
       
   455      * @param string $id
       
   456      * @param Definition $definition
       
   457      * @return string
       
   458      */
       
   459     private function addService($id, $definition)
       
   460     {
       
   461         $name = Container::camelize($id);
       
   462         $this->definitionVariables = new \SplObjectStorage();
       
   463         $this->referenceVariables = array();
       
   464         $this->variableCount = 0;
       
   465 
       
   466         $return = '';
       
   467         if ($definition->isSynthetic()) {
       
   468             $return = sprintf('@throws \RuntimeException always since this service is expected to be injected dynamically');
       
   469         } elseif ($class = $definition->getClass()) {
       
   470             $return = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'Object' : $class, $class);
       
   471         } elseif ($definition->getFactoryClass()) {
       
   472             $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
       
   473         } elseif ($definition->getFactoryService()) {
       
   474             $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
       
   475         }
       
   476 
       
   477         $doc = '';
       
   478         if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) {
       
   479             $doc .= <<<EOF
       
   480 
       
   481      *
       
   482      * This service is shared.
       
   483      * This method always returns the same instance of the service.
       
   484 EOF;
       
   485         }
       
   486 
       
   487         if (!$definition->isPublic()) {
       
   488             $doc .= <<<EOF
       
   489 
       
   490      *
       
   491      * This service is private.
       
   492      * If you want to be able to request this service from the container directly,
       
   493      * make it public, otherwise you might end up with broken code.
       
   494 EOF;
       
   495         }
       
   496 
       
   497         $code = <<<EOF
       
   498 
       
   499     /**
       
   500      * Gets the '$id' service.$doc
       
   501      *
       
   502      * $return
       
   503      */
       
   504     protected function get{$name}Service()
       
   505     {
       
   506 
       
   507 EOF;
       
   508 
       
   509         $scope = $definition->getScope();
       
   510         if (ContainerInterface::SCOPE_CONTAINER !== $scope && ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
       
   511             $code .= <<<EOF
       
   512         if (!isset(\$this->scopedServices['$scope'])) {
       
   513             throw new InactiveScopeException('$id', '$scope');
       
   514         }
       
   515 
       
   516 
       
   517 EOF;
       
   518         }
       
   519 
       
   520         if ($definition->isSynthetic()) {
       
   521             $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);
       
   522         } else {
       
   523             $code .=
       
   524                 $this->addServiceInclude($id, $definition).
       
   525                 $this->addServiceLocalTempVariables($id, $definition).
       
   526                 $this->addServiceInlinedDefinitions($id, $definition).
       
   527                 $this->addServiceInstance($id, $definition).
       
   528                 $this->addServiceInlinedDefinitionsSetup($id, $definition).
       
   529                 $this->addServiceMethodCalls($id, $definition).
       
   530                 $this->addServiceProperties($id, $definition).
       
   531                 $this->addServiceConfigurator($id, $definition).
       
   532                 $this->addServiceReturn($id, $definition)
       
   533             ;
       
   534         }
       
   535 
       
   536         $this->definitionVariables = null;
       
   537         $this->referenceVariables = null;
       
   538 
       
   539         return $code;
       
   540     }
       
   541 
       
   542     /**
       
   543      * Adds a service alias.
       
   544      *
       
   545      * @param string $alias
       
   546      * @param string $id
       
   547      * @return string
       
   548      */
       
   549     private function addServiceAlias($alias, $id)
       
   550     {
       
   551         $name = Container::camelize($alias);
       
   552         $type = 'Object';
       
   553 
       
   554         if ($this->container->hasDefinition($id)) {
       
   555             $class = $this->container->getDefinition($id)->getClass();
       
   556             $type = 0 === strpos($class, '%') ? 'Object' : $class;
       
   557         }
       
   558 
       
   559         return <<<EOF
       
   560 
       
   561     /**
       
   562      * Gets the $alias service alias.
       
   563      *
       
   564      * @return $type An instance of the $id service
       
   565      */
       
   566     protected function get{$name}Service()
       
   567     {
       
   568         return {$this->getServiceCall($id)};
       
   569     }
       
   570 
       
   571 EOF;
       
   572     }
       
   573 
       
   574     /**
       
   575      * Adds multiple services
       
   576      *
       
   577      * @return string
       
   578      */
       
   579     private function addServices()
       
   580     {
       
   581         $publicServices = $privateServices = $aliasServices = '';
       
   582         $definitions = $this->container->getDefinitions();
       
   583         ksort($definitions);
       
   584         foreach ($definitions as $id => $definition) {
       
   585             if ($definition->isPublic()) {
       
   586                 $publicServices .= $this->addService($id, $definition);
       
   587             } else {
       
   588                 $privateServices .= $this->addService($id, $definition);
       
   589             }
       
   590         }
       
   591 
       
   592         $aliases = $this->container->getAliases();
       
   593         ksort($aliases);
       
   594         foreach ($aliases as $alias => $id) {
       
   595             $aliasServices .= $this->addServiceAlias($alias, $id);
       
   596         }
       
   597 
       
   598         return $publicServices.$aliasServices.$privateServices;
       
   599     }
       
   600 
       
   601     /**
       
   602      * Adds the class headers.
       
   603      *
       
   604      * @param string $class Class name
       
   605      * @param string $baseClass The name of the base class
       
   606      * @return string
       
   607      */
       
   608     private function startClass($class, $baseClass)
       
   609     {
       
   610         $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
       
   611 
       
   612         return <<<EOF
       
   613 <?php
       
   614 
       
   615 use Symfony\Component\DependencyInjection\ContainerInterface;
       
   616 use Symfony\Component\DependencyInjection\Container;
       
   617 use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
       
   618 use Symfony\Component\DependencyInjection\Reference;
       
   619 use Symfony\Component\DependencyInjection\Parameter;
       
   620 $bagClass
       
   621 
       
   622 /**
       
   623  * $class
       
   624  *
       
   625  * This class has been auto-generated
       
   626  * by the Symfony Dependency Injection Component.
       
   627  */
       
   628 class $class extends $baseClass
       
   629 {
       
   630 EOF;
       
   631     }
       
   632 
       
   633     /**
       
   634      * Adds the constructor.
       
   635      *
       
   636      * @return string
       
   637      */
       
   638     private function addConstructor()
       
   639     {
       
   640         $code = <<<EOF
       
   641 
       
   642     /**
       
   643      * Constructor.
       
   644      */
       
   645     public function __construct()
       
   646     {
       
   647         parent::__construct(new ParameterBag(\$this->getDefaultParameters()));
       
   648 
       
   649 EOF;
       
   650 
       
   651         if (count($scopes = $this->container->getScopes()) > 0) {
       
   652             $code .= "\n";
       
   653             $code .= "        \$this->scopes = ".$this->dumpValue($scopes).";\n";
       
   654             $code .= "        \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
       
   655         }
       
   656 
       
   657         $code .= <<<EOF
       
   658     }
       
   659 
       
   660 EOF;
       
   661 
       
   662         return $code;
       
   663     }
       
   664 
       
   665     /**
       
   666      * Adds the constructor for a frozen container.
       
   667      *
       
   668      * @return string
       
   669      */
       
   670     private function addFrozenConstructor()
       
   671     {
       
   672         $code = <<<EOF
       
   673 
       
   674     /**
       
   675      * Constructor.
       
   676      */
       
   677     public function __construct()
       
   678     {
       
   679         \$this->parameters = \$this->getDefaultParameters();
       
   680 
       
   681         \$this->services =
       
   682         \$this->scopedServices =
       
   683         \$this->scopeStacks = array();
       
   684 
       
   685         \$this->set('service_container', \$this);
       
   686 
       
   687 EOF;
       
   688 
       
   689         $code .= "\n";
       
   690         if (count($scopes = $this->container->getScopes()) > 0) {
       
   691             $code .= "        \$this->scopes = ".$this->dumpValue($scopes).";\n";
       
   692             $code .= "        \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
       
   693         } else {
       
   694             $code .= "        \$this->scopes = array();\n";
       
   695             $code .= "        \$this->scopeChildren = array();\n";
       
   696         }
       
   697 
       
   698         $code .= <<<EOF
       
   699     }
       
   700 
       
   701 EOF;
       
   702 
       
   703         return $code;
       
   704     }
       
   705 
       
   706     /**
       
   707      * Adds default parameters method.
       
   708      *
       
   709      * @return string
       
   710      */
       
   711     private function addDefaultParametersMethod()
       
   712     {
       
   713         if (!$this->container->getParameterBag()->all()) {
       
   714             return '';
       
   715         }
       
   716 
       
   717         $parameters = $this->exportParameters($this->container->getParameterBag()->all());
       
   718 
       
   719         $code = '';
       
   720         if ($this->container->isFrozen()) {
       
   721             $code .= <<<EOF
       
   722 
       
   723     /**
       
   724      * {@inheritdoc}
       
   725      */
       
   726     public function getParameter(\$name)
       
   727     {
       
   728         \$name = strtolower(\$name);
       
   729 
       
   730         if (!array_key_exists(\$name, \$this->parameters)) {
       
   731             throw new \InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
       
   732         }
       
   733 
       
   734         return \$this->parameters[\$name];
       
   735     }
       
   736 
       
   737     /**
       
   738      * {@inheritdoc}
       
   739      */
       
   740     public function hasParameter(\$name)
       
   741     {
       
   742         return array_key_exists(strtolower(\$name), \$this->parameters);
       
   743     }
       
   744 
       
   745     /**
       
   746      * {@inheritdoc}
       
   747      */
       
   748     public function setParameter(\$name, \$value)
       
   749     {
       
   750         throw new \LogicException('Impossible to call set() on a frozen ParameterBag.');
       
   751     }
       
   752 
       
   753     /**
       
   754      * {@inheritDoc}
       
   755      */
       
   756     public function getParameterBag()
       
   757     {
       
   758         if (null === \$this->parameterBag) {
       
   759             \$this->parameterBag = new FrozenParameterBag(\$this->parameters);
       
   760         }
       
   761 
       
   762         return \$this->parameterBag;
       
   763     }
       
   764 EOF;
       
   765         }
       
   766 
       
   767         $code .= <<<EOF
       
   768 
       
   769     /**
       
   770      * Gets the default parameters.
       
   771      *
       
   772      * @return array An array of the default parameters
       
   773      */
       
   774     protected function getDefaultParameters()
       
   775     {
       
   776         return $parameters;
       
   777     }
       
   778 
       
   779 EOF;
       
   780 
       
   781         return $code;
       
   782     }
       
   783 
       
   784     /**
       
   785      * Exports parameters.
       
   786      *
       
   787      * @param array $parameters
       
   788      * @param string $path
       
   789      * @param integer $indent
       
   790      * @return string
       
   791      */
       
   792     private function exportParameters($parameters, $path = '', $indent = 12)
       
   793     {
       
   794         $php = array();
       
   795         foreach ($parameters as $key => $value) {
       
   796             if (is_array($value)) {
       
   797                 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
       
   798             } elseif ($value instanceof Variable) {
       
   799                 throw new \InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
       
   800             } elseif ($value instanceof Definition) {
       
   801                 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));
       
   802             } elseif ($value instanceof Reference) {
       
   803                 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));
       
   804             } else {
       
   805                 $value = var_export($value, true);
       
   806             }
       
   807 
       
   808             $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
       
   809         }
       
   810 
       
   811         return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
       
   812     }
       
   813 
       
   814     /**
       
   815      * Ends the class definition.
       
   816      *
       
   817      * @return string
       
   818      */
       
   819     private function endClass()
       
   820     {
       
   821         return <<<EOF
       
   822 }
       
   823 
       
   824 EOF;
       
   825     }
       
   826 
       
   827     /**
       
   828      * Wraps the service conditionals.
       
   829      *
       
   830      * @param string $value
       
   831      * @param string $code
       
   832      * @return string
       
   833      */
       
   834     private function wrapServiceConditionals($value, $code)
       
   835     {
       
   836         if (!$services = ContainerBuilder::getServiceConditionals($value)) {
       
   837             return $code;
       
   838         }
       
   839 
       
   840         $conditions = array();
       
   841         foreach ($services as $service) {
       
   842             $conditions[] = sprintf("\$this->has('%s')", $service);
       
   843         }
       
   844 
       
   845         // re-indent the wrapped code
       
   846         $code = implode("\n", array_map(function ($line) { return $line ? '    '.$line : $line; }, explode("\n", $code)));
       
   847 
       
   848         return sprintf("        if (%s) {\n%s        }\n", implode(' && ', $conditions), $code);
       
   849     }
       
   850 
       
   851     /**
       
   852      * Builds service calls from arguments
       
   853      *
       
   854      * @param array  $arguments
       
   855      * @param string $calls    By reference
       
   856      * @param string $behavior By reference
       
   857      * @return void
       
   858      */
       
   859     private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
       
   860     {
       
   861         foreach ($arguments as $argument) {
       
   862             if (is_array($argument)) {
       
   863                 $this->getServiceCallsFromArguments($argument, $calls, $behavior);
       
   864             } else if ($argument instanceof Reference) {
       
   865                 $id = (string) $argument;
       
   866 
       
   867                 if (!isset($calls[$id])) {
       
   868                     $calls[$id] = 0;
       
   869                 }
       
   870                 if (!isset($behavior[$id])) {
       
   871                     $behavior[$id] = $argument->getInvalidBehavior();
       
   872                 } else if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
       
   873                     $behavior[$id] = $argument->getInvalidBehavior();
       
   874                 }
       
   875 
       
   876                 $calls[$id] += 1;
       
   877             }
       
   878         }
       
   879     }
       
   880 
       
   881     /**
       
   882      * Returns the inline definition
       
   883      *
       
   884      * @param Definition $definition
       
   885      * @return array
       
   886      */
       
   887     private function getInlinedDefinitions(Definition $definition)
       
   888     {
       
   889         if (false === $this->inlinedDefinitions->contains($definition)) {
       
   890             $definitions = array_merge(
       
   891                 $this->getDefinitionsFromArguments($definition->getArguments()),
       
   892                 $this->getDefinitionsFromArguments($definition->getMethodCalls()),
       
   893                 $this->getDefinitionsFromArguments($definition->getProperties())
       
   894             );
       
   895 
       
   896             $this->inlinedDefinitions->offsetSet($definition, $definitions);
       
   897 
       
   898             return $definitions;
       
   899         }
       
   900 
       
   901         return $this->inlinedDefinitions->offsetGet($definition);
       
   902     }
       
   903 
       
   904     /**
       
   905      * Gets the definition from arguments
       
   906      *
       
   907      * @param array $arguments
       
   908      * @return array
       
   909      */
       
   910     private function getDefinitionsFromArguments(array $arguments)
       
   911     {
       
   912         $definitions = array();
       
   913         foreach ($arguments as $argument) {
       
   914             if (is_array($argument)) {
       
   915                 $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
       
   916             } else if ($argument instanceof Definition) {
       
   917                 $definitions = array_merge(
       
   918                     $definitions,
       
   919                     $this->getInlinedDefinitions($argument),
       
   920                     array($argument)
       
   921                 );
       
   922             }
       
   923         }
       
   924 
       
   925         return $definitions;
       
   926     }
       
   927 
       
   928     /**
       
   929      * Checks if a service id has a reference
       
   930      *
       
   931      * @param string $id
       
   932      * @param array $arguments
       
   933      * @return Boolean
       
   934      */
       
   935     private function hasReference($id, array $arguments)
       
   936     {
       
   937         foreach ($arguments as $argument) {
       
   938             if (is_array($argument)) {
       
   939                 if ($this->hasReference($id, $argument)) {
       
   940                     return true;
       
   941                 }
       
   942             } else if ($argument instanceof Reference) {
       
   943                 if ($id === (string) $argument) {
       
   944                     return true;
       
   945                 }
       
   946             }
       
   947         }
       
   948 
       
   949         return false;
       
   950     }
       
   951 
       
   952     /**
       
   953      * Dumps values.
       
   954      *
       
   955      * @param array $value
       
   956      * @param Boolean $interpolate
       
   957      * @return string
       
   958      */
       
   959     private function dumpValue($value, $interpolate = true)
       
   960     {
       
   961         if (is_array($value)) {
       
   962             $code = array();
       
   963             foreach ($value as $k => $v) {
       
   964                 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
       
   965             }
       
   966 
       
   967             return sprintf('array(%s)', implode(', ', $code));
       
   968         } elseif (is_object($value) && $value instanceof Definition) {
       
   969             if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
       
   970                 return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
       
   971             }
       
   972             if (count($value->getMethodCalls()) > 0) {
       
   973                 throw new \RuntimeException('Cannot dump definitions which have method calls.');
       
   974             }
       
   975             if (null !== $value->getConfigurator()) {
       
   976                 throw new \RuntimeException('Cannot dump definitions which have a configurator.');
       
   977             }
       
   978 
       
   979             $arguments = array();
       
   980             foreach ($value->getArguments() as $argument) {
       
   981                 $arguments[] = $this->dumpValue($argument);
       
   982             }
       
   983             $class = $this->dumpValue($value->getClass());
       
   984 
       
   985             if (false !== strpos($class, '$')) {
       
   986                 throw new \RuntimeException('Cannot dump definitions which have a variable class name.');
       
   987             }
       
   988 
       
   989             if (null !== $value->getFactoryMethod()) {
       
   990                 if (null !== $value->getFactoryClass()) {
       
   991                     return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
       
   992                 } elseif (null !== $value->getFactoryService()) {
       
   993                     return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments));
       
   994                 } else {
       
   995                     throw new \RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
       
   996                 }
       
   997             }
       
   998 
       
   999             return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
       
  1000         } elseif (is_object($value) && $value instanceof Variable) {
       
  1001             return '$'.$value;
       
  1002         } elseif (is_object($value) && $value instanceof Reference) {
       
  1003             if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
       
  1004                 return $this->dumpValue($this->referenceVariables[$id], $interpolate);
       
  1005             }
       
  1006 
       
  1007             return $this->getServiceCall((string) $value, $value);
       
  1008         } elseif (is_object($value) && $value instanceof Parameter) {
       
  1009             return $this->dumpParameter($value);
       
  1010         } elseif (true === $interpolate && is_string($value)) {
       
  1011             if (preg_match('/^%([^%]+)%$/', $value, $match)) {
       
  1012                 // we do this to deal with non string values (Boolean, integer, ...)
       
  1013                 // the preg_replace_callback converts them to strings
       
  1014                 return $this->dumpParameter(strtolower($match[1]));
       
  1015             } else {
       
  1016                 $that = $this;
       
  1017                 $replaceParameters = function ($match) use ($that)
       
  1018                 {
       
  1019                     return "'.".$that->dumpParameter(strtolower($match[2])).".'";
       
  1020                 };
       
  1021 
       
  1022                 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, var_export($value, true)));
       
  1023 
       
  1024                 // optimize string
       
  1025                 $code = preg_replace(array("/^''\./", "/\.''$/", "/'\.'/", "/\.''\./"), array('', '', '', '.'), $code);
       
  1026 
       
  1027                 return $code;
       
  1028             }
       
  1029         } elseif (is_object($value) || is_resource($value)) {
       
  1030             throw new \RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
       
  1031         } else {
       
  1032             return var_export($value, true);
       
  1033         }
       
  1034     }
       
  1035 
       
  1036     /**
       
  1037      * Dumps a parameter
       
  1038      *
       
  1039      * @param string $name
       
  1040      * @return string
       
  1041      */
       
  1042     public function dumpParameter($name)
       
  1043     {
       
  1044         if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
       
  1045             return $this->dumpValue($this->container->getParameter($name), false);
       
  1046         }
       
  1047 
       
  1048         return sprintf("\$this->getParameter('%s')", strtolower($name));
       
  1049     }
       
  1050 
       
  1051     /**
       
  1052      * Gets a service call
       
  1053      *
       
  1054      * @param string    $id
       
  1055      * @param Reference $reference
       
  1056      * @return string
       
  1057      */
       
  1058     private function getServiceCall($id, Reference $reference = null)
       
  1059     {
       
  1060         if ('service_container' === $id) {
       
  1061             return '$this';
       
  1062         }
       
  1063 
       
  1064         if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
       
  1065             return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
       
  1066         } else {
       
  1067             if ($this->container->hasAlias($id)) {
       
  1068                 $id = (string) $this->container->getAlias($id);
       
  1069             }
       
  1070 
       
  1071             return sprintf('$this->get(\'%s\')', $id);
       
  1072         }
       
  1073     }
       
  1074 
       
  1075     /**
       
  1076      * Returns the next name to use
       
  1077      *
       
  1078      * @return string
       
  1079      */
       
  1080     private function getNextVariableName()
       
  1081     {
       
  1082         $firstChars = self::FIRST_CHARS;
       
  1083         $firstCharsLength = strlen($firstChars);
       
  1084         $nonFirstChars = self::NON_FIRST_CHARS;
       
  1085         $nonFirstCharsLength = strlen($nonFirstChars);
       
  1086 
       
  1087         while (true) {
       
  1088             $name = '';
       
  1089             $i = $this->variableCount;
       
  1090 
       
  1091             if ('' === $name) {
       
  1092                 $name .= $firstChars[$i%$firstCharsLength];
       
  1093                 $i = intval($i/$firstCharsLength);
       
  1094             }
       
  1095 
       
  1096             while ($i > 0) {
       
  1097                 $i -= 1;
       
  1098                 $name .= $nonFirstChars[$i%$nonFirstCharsLength];
       
  1099                 $i = intval($i/$nonFirstCharsLength);
       
  1100             }
       
  1101 
       
  1102             $this->variableCount += 1;
       
  1103 
       
  1104             // check that the name is not reserved
       
  1105             if (in_array($name, $this->reservedVariables, true)) {
       
  1106                 continue;
       
  1107             }
       
  1108 
       
  1109             return $name;
       
  1110         }
       
  1111     }
       
  1112 }