Source of file PhpDumper.php
Size: 42,124 Bytes - Last Modified: 2013-07-17T08:22:21+02:00
/home/theseer/Downloads/Symfony/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301 | <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\DependencyInjection\Dumper; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; /** * PhpDumper dumps a service container as a PHP class. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @api */ class PhpDumper extends Dumper { /** * Characters that might appear in the generated variable name as first character * @var string */ const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; /** * Characters that might appear in the generated variable name as any but the first character * @var string */ const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; private $inlinedDefinitions; private $definitionVariables; private $referenceVariables; private $variableCount; private $reservedVariables = array('instance', 'class'); /** * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface */ private $proxyDumper; /** * {@inheritDoc} * * @api */ public function __construct(ContainerBuilder $container) { parent::__construct($container); $this->inlinedDefinitions = new \SplObjectStorage; } /** * Sets the dumper to be used when dumping proxies in the generated container. * * @param ProxyDumper $proxyDumper */ public function setProxyDumper(ProxyDumper $proxyDumper) { $this->proxyDumper = $proxyDumper; } /** * Dumps the service container as a PHP class. * * Available options: * * * class: The class name * * base_class: The base class name * * @param array $options An array of options * * @return string A PHP class representing of the service container * * @api */ public function dump(array $options = array()) { $options = array_merge(array( 'class' => 'ProjectServiceContainer', 'base_class' => 'Container', ), $options); $code = $this->startClass($options['class'], $options['base_class']); if ($this->container->isFrozen()) { $code .= $this->addFrozenConstructor(); } else { $code .= $this->addConstructor(); } $code .= $this->addServices(). $this->addDefaultParametersMethod(). $this->endClass(). $this->addProxyClasses() ; return $code; } /** * Retrieves the currently set proxy dumper or instantiates one. * * @return ProxyDumper */ private function getProxyDumper() { if (!$this->proxyDumper) { $this->proxyDumper = new NullDumper(); } return $this->proxyDumper; } /** * Generates Service local temp variables. * * @param string $cId * @param string $definition * * @return string */ private function addServiceLocalTempVariables($cId, $definition) { static $template = " \$%s = %s;\n"; $localDefinitions = array_merge( array($definition), $this->getInlinedDefinitions($definition) ); $calls = $behavior = array(); foreach ($localDefinitions as $iDefinition) { $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior); $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior); $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior); } $code = ''; foreach ($calls as $id => $callCount) { if ('service_container' === $id || $id === $cId) { continue; } if ($callCount > 1) { $name = $this->getNextVariableName(); $this->referenceVariables[$id] = new Variable($name); if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) { $code .= sprintf($template, $name, $this->getServiceCall($id)); } else { $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE))); } } } if ('' !== $code) { $code .= "\n"; } return $code; } /** * Generates code for the proxies to be attached after the container class * * @return string */ private function addProxyClasses() { /* @var $proxyDefinitions Definition[] */ $definitions = array_filter( $this->container->getDefinitions(), array($this->getProxyDumper(), 'isProxyCandidate') ); $code = ''; foreach ($definitions as $definition) { $code .= "\n" . $this->getProxyDumper()->getProxyCode($definition); } return $code; } /** * Generates the require_once statement for service includes. * * @param string $id The service id * @param Definition $definition * * @return string */ private function addServiceInclude($id, $definition) { $template = " require_once %s;\n"; $code = ''; if (null !== $file = $definition->getFile()) { $code .= sprintf($template, $this->dumpValue($file)); } foreach ($this->getInlinedDefinitions($definition) as $definition) { if (null !== $file = $definition->getFile()) { $code .= sprintf($template, $this->dumpValue($file)); } } if ('' !== $code) { $code .= "\n"; } return $code; } /** * Generates the inline definition of a service. * * @param string $id * @param Definition $definition * * @return string * * @throws RuntimeException When the factory definition is incomplete * @throws ServiceCircularReferenceException When a circular reference is detected */ private function addServiceInlinedDefinitions($id, $definition) { $code = ''; $variableMap = $this->definitionVariables; $nbOccurrences = new \SplObjectStorage(); $processed = new \SplObjectStorage(); $inlinedDefinitions = $this->getInlinedDefinitions($definition); foreach ($inlinedDefinitions as $definition) { if (false === $nbOccurrences->contains($definition)) { $nbOccurrences->offsetSet($definition, 1); } else { $i = $nbOccurrences->offsetGet($definition); $nbOccurrences->offsetSet($definition, $i + 1); } } foreach ($inlinedDefinitions as $sDefinition) { if ($processed->contains($sDefinition)) { continue; } $processed->offsetSet($sDefinition); $class = $this->dumpValue($sDefinition->getClass()); if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { $name = $this->getNextVariableName(); $variableMap->offsetSet($sDefinition, new Variable($name)); // a construct like: // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); // this is an indication for a wrong implementation, you can circumvent this problem // by setting up your service structure like this: // $b = new ServiceB(); // $a = new ServiceA(ServiceB $b); // $b->setServiceA(ServiceA $a); if ($this->hasReference($id, $sDefinition->getArguments())) { throw new ServiceCircularReferenceException($id, array($id)); } $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = '); if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) { $code .= $this->addServiceMethodCalls(null, $sDefinition, $name); $code .= $this->addServiceProperties(null, $sDefinition, $name); $code .= $this->addServiceConfigurator(null, $sDefinition, $name); } $code .= "\n"; } } return $code; } /** * Adds the service return statement. * * @param string $id Service id * @param Definition $definition * * @return string */ private function addServiceReturn($id, $definition) { if ($this->isSimpleInstance($id, $definition)) { return " }\n"; } return "\n return \$instance;\n }\n"; } /** * Generates the service instance. * * @param string $id * @param Definition $definition * * @return string * * @throws InvalidArgumentException * @throws RuntimeException */ private function addServiceInstance($id, $definition) { $class = $this->dumpValue($definition->getClass()); 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)) { throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); } $simple = $this->isSimpleInstance($id, $definition); $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); $instantiation = ''; if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance'); } elseif (!$simple) { $instantiation = '$instance'; } $return = ''; if ($simple) { $return = 'return '; } else { $instantiation .= ' = '; } $code = $this->addNewInstance($id, $definition, $return, $instantiation); if (!$simple) { $code .= "\n"; } return $code; } /** * Checks if the definition is a simple instance. * * @param string $id * @param Definition $definition * * @return Boolean */ private function isSimpleInstance($id, $definition) { foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) { if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) { continue; } if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) { return false; } } return true; } /** * Adds method calls to a service definition. * * @param string $id * @param Definition $definition * @param string $variableName * * @return string */ private function addServiceMethodCalls($id, $definition, $variableName = 'instance') { $calls = ''; foreach ($definition->getMethodCalls() as $call) { $arguments = array(); foreach ($call[1] as $value) { $arguments[] = $this->dumpValue($value); } $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments))); } return $calls; } private function addServiceProperties($id, $definition, $variableName = 'instance') { $code = ''; foreach ($definition->getProperties() as $name => $value) { $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value)); } return $code; } /** * Generates the inline definition setup. * * @param string $id * @param Definition $definition * @return string */ private function addServiceInlinedDefinitionsSetup($id, $definition) { $this->referenceVariables[$id] = new Variable('instance'); $code = ''; $processed = new \SplObjectStorage(); foreach ($this->getInlinedDefinitions($definition) as $iDefinition) { if ($processed->contains($iDefinition)) { continue; } $processed->offsetSet($iDefinition); if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) { continue; } $name = (string) $this->definitionVariables->offsetGet($iDefinition); $code .= $this->addServiceMethodCalls(null, $iDefinition, $name); $code .= $this->addServiceProperties(null, $iDefinition, $name); $code .= $this->addServiceConfigurator(null, $iDefinition, $name); } if ('' !== $code) { $code .= "\n"; } return $code; } /** * Adds configurator definition * * @param string $id * @param Definition $definition * @param string $variableName * * @return string */ private function addServiceConfigurator($id, $definition, $variableName = 'instance') { if (!$callable = $definition->getConfigurator()) { return ''; } if (is_array($callable)) { if ($callable[0] instanceof Reference) { return sprintf(" %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $callable[1], $variableName); } return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } return sprintf(" %s(\$%s);\n", $callable, $variableName); } /** * Adds a service * * @param string $id * @param Definition $definition * * @return string */ private function addService($id, $definition) { $name = Container::camelize($id); $this->definitionVariables = new \SplObjectStorage(); $this->referenceVariables = array(); $this->variableCount = 0; $return = array(); if ($definition->isSynthetic()) { $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically'; } elseif ($class = $definition->getClass()) { $return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : $class, $class); } elseif ($definition->getFactoryClass()) { $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod()); } elseif ($definition->getFactoryService()) { $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod()); } $scope = $definition->getScope(); if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { if ($return && 0 === strpos($return[count($return) - 1], '@return')) { $return[] = ''; } $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope); } $return = implode("\n * ", $return); $doc = ''; if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) { $doc .= <<<EOF * * This service is shared. * This method always returns the same instance of the service. EOF; } if (!$definition->isPublic()) { $doc .= <<<EOF * * This service is private. * If you want to be able to request this service from the container directly, * make it public, otherwise you might end up with broken code. EOF; } if ($definition->isLazy()) { $lazyInitialization = '$lazyLoad = true'; $lazyInitializationDoc = "\n * @param boolean \$lazyLoad whether to try lazy-loading the service with a proxy\n *"; } else { $lazyInitialization = ''; $lazyInitializationDoc = ''; } // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); $visibility = $isProxyCandidate ? 'public' : 'protected'; $code = <<<EOF /** * Gets the '$id' service.$doc *$lazyInitializationDoc * $return */ {$visibility} function get{$name}Service($lazyInitialization) { EOF; $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : ''; if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { $code .= <<<EOF if (!isset(\$this->scopedServices['$scope'])) { throw new InactiveScopeException('$id', '$scope'); } EOF; } if ($definition->isSynthetic()) { $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); } else { $code .= $this->addServiceInclude($id, $definition). $this->addServiceLocalTempVariables($id, $definition). $this->addServiceInlinedDefinitions($id, $definition). $this->addServiceInstance($id, $definition). $this->addServiceInlinedDefinitionsSetup($id, $definition). $this->addServiceMethodCalls($id, $definition). $this->addServiceProperties($id, $definition). $this->addServiceConfigurator($id, $definition). $this->addServiceReturn($id, $definition) ; } $this->definitionVariables = null; $this->referenceVariables = null; return $code; } /** * Adds multiple services * * @return string */ private function addServices() { $publicServices = $privateServices = $synchronizers = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { if ($definition->isPublic()) { $publicServices .= $this->addService($id, $definition); } else { $privateServices .= $this->addService($id, $definition); } $synchronizers .= $this->addServiceSynchronizer($id, $definition); } return $publicServices.$synchronizers.$privateServices; } /** * Adds synchronizer methods. * * @param string $id A service identifier * @param Definition $definition A Definition instance */ private function addServiceSynchronizer($id, Definition $definition) { if (!$definition->isSynchronized()) { return; } $code = ''; foreach ($this->container->getDefinitions() as $definitionId => $definition) { foreach ($definition->getMethodCalls() as $call) { foreach ($call[1] as $argument) { if ($argument instanceof Reference && $id == (string) $argument) { $arguments = array(); foreach ($call[1] as $value) { $arguments[] = $this->dumpValue($value); } $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments))); $code .= <<<EOF if (\$this->initialized('$definitionId')) { $call } EOF; } } } } if (!$code) { return; } $name = Container::camelize($id); return <<<EOF /** * Updates the '$id' service. */ protected function synchronize{$name}Service() { $code } EOF; } private function addNewInstance($id, Definition $definition, $return, $instantiation) { $class = $this->dumpValue($definition->getClass()); $arguments = array(); foreach ($definition->getArguments() as $value) { $arguments[] = $this->dumpValue($value); } if (null !== $definition->getFactoryMethod()) { if (null !== $definition->getFactoryClass()) { return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : ''); } if (null !== $definition->getFactoryService()) { return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments)); } throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id)); } if (false !== strpos($class, '$')) { return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments)); } return sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments)); } /** * Adds the class headers. * * @param string $class Class name * @param string $baseClass The name of the base class * * @return string */ private function startClass($class, $baseClass) { $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; return <<<EOF <?php use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Exception\InactiveScopeException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Parameter; $bagClass /** * $class * * This class has been auto-generated * by the Symfony Dependency Injection Component. */ class $class extends $baseClass { EOF; } /** * Adds the constructor. * * @return string */ private function addConstructor() { $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; $code = <<<EOF /** * Constructor. */ public function __construct() { parent::__construct($arguments); EOF; if (count($scopes = $this->container->getScopes()) > 0) { $code .= "\n"; $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n"; $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n"; } $code .= $this->addMethodMap(); $code .= $this->addAliases(); $code .= <<<EOF } EOF; return $code; } /** * Adds the constructor for a frozen container. * * @return string */ private function addFrozenConstructor() { $code = <<<EOF /** * Constructor. */ public function __construct() { EOF; if ($this->container->getParameterBag()->all()) { $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; } $code .= <<<EOF \$this->services = \$this->scopedServices = \$this->scopeStacks = array(); \$this->set('service_container', \$this); EOF; $code .= "\n"; if (count($scopes = $this->container->getScopes()) > 0) { $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n"; $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n"; } else { $code .= " \$this->scopes = array();\n"; $code .= " \$this->scopeChildren = array();\n"; } $code .= $this->addMethodMap(); $code .= $this->addAliases(); $code .= <<<EOF } EOF; return $code; } /** * Adds the methodMap property definition * * @return string */ private function addMethodMap() { if (!$definitions = $this->container->getDefinitions()) { return ''; } $code = " \$this->methodMap = array(\n"; ksort($definitions); foreach ($definitions as $id => $definition) { $code .= ' '.var_export($id, true).' => '.var_export('get'.Container::camelize($id).'Service', true).",\n"; } return $code . " );\n"; } /** * Adds the aliases property definition * * @return string */ private function addAliases() { if (!$aliases = $this->container->getAliases()) { if ($this->container->isFrozen()) { return "\n \$this->aliases = array();\n"; } else { return ''; } } $code = " \$this->aliases = array(\n"; ksort($aliases); foreach ($aliases as $alias => $id) { $id = (string) $id; while (isset($aliases[$id])) { $id = (string) $aliases[$id]; } $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n"; } return $code . " );\n"; } /** * Adds default parameters method. * * @return string */ private function addDefaultParametersMethod() { if (!$this->container->getParameterBag()->all()) { return ''; } $parameters = $this->exportParameters($this->container->getParameterBag()->all()); $code = ''; if ($this->container->isFrozen()) { $code .= <<<EOF /** * {@inheritdoc} */ public function getParameter(\$name) { \$name = strtolower(\$name); if (!(isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) { throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name)); } return \$this->parameters[\$name]; } /** * {@inheritdoc} */ public function hasParameter(\$name) { \$name = strtolower(\$name); return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters); } /** * {@inheritdoc} */ public function setParameter(\$name, \$value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } /** * {@inheritDoc} */ public function getParameterBag() { if (null === \$this->parameterBag) { \$this->parameterBag = new FrozenParameterBag(\$this->parameters); } return \$this->parameterBag; } EOF; } $code .= <<<EOF /** * Gets the default parameters. * * @return array An array of the default parameters */ protected function getDefaultParameters() { return $parameters; } EOF; return $code; } /** * Exports parameters. * * @param array $parameters * @param string $path * @param integer $indent * * @return string * * @throws InvalidArgumentException */ private function exportParameters($parameters, $path = '', $indent = 12) { $php = array(); foreach ($parameters as $key => $value) { if (is_array($value)) { $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); } elseif ($value instanceof Variable) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); } elseif ($value instanceof Definition) { 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)); } elseif ($value instanceof Reference) { 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)); } else { $value = var_export($value, true); } $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value); } return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4)); } /** * Ends the class definition. * * @return string */ private function endClass() { return <<<EOF } EOF; } /** * Wraps the service conditionals. * * @param string $value * @param string $code * * @return string */ private function wrapServiceConditionals($value, $code) { if (!$services = ContainerBuilder::getServiceConditionals($value)) { return $code; } $conditions = array(); foreach ($services as $service) { $conditions[] = sprintf("\$this->has('%s')", $service); } // re-indent the wrapped code $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code); } /** * Builds service calls from arguments. * * @param array $arguments * @param array &$calls By reference * @param array &$behavior By reference */ private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior) { foreach ($arguments as $argument) { if (is_array($argument)) { $this->getServiceCallsFromArguments($argument, $calls, $behavior); } elseif ($argument instanceof Reference) { $id = (string) $argument; if (!isset($calls[$id])) { $calls[$id] = 0; } if (!isset($behavior[$id])) { $behavior[$id] = $argument->getInvalidBehavior(); } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) { $behavior[$id] = $argument->getInvalidBehavior(); } $calls[$id] += 1; } } } /** * Returns the inline definition. * * @param Definition $definition * * @return array */ private function getInlinedDefinitions(Definition $definition) { if (false === $this->inlinedDefinitions->contains($definition)) { $definitions = array_merge( $this->getDefinitionsFromArguments($definition->getArguments()), $this->getDefinitionsFromArguments($definition->getMethodCalls()), $this->getDefinitionsFromArguments($definition->getProperties()) ); $this->inlinedDefinitions->offsetSet($definition, $definitions); return $definitions; } return $this->inlinedDefinitions->offsetGet($definition); } /** * Gets the definition from arguments. * * @param array $arguments * * @return array */ private function getDefinitionsFromArguments(array $arguments) { $definitions = array(); foreach ($arguments as $argument) { if (is_array($argument)) { $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument)); } elseif ($argument instanceof Definition) { $definitions = array_merge( $definitions, $this->getInlinedDefinitions($argument), array($argument) ); } } return $definitions; } /** * Checks if a service id has a reference. * * @param string $id * @param array $arguments * @param Boolean $deep * @param array $visited * * @return Boolean */ private function hasReference($id, array $arguments, $deep = false, $visited = array()) { foreach ($arguments as $argument) { if (is_array($argument)) { if ($this->hasReference($id, $argument, $deep, $visited)) { return true; } } elseif ($argument instanceof Reference) { if ($id === (string) $argument) { return true; } if ($deep && !isset($visited[(string) $argument])) { $visited[(string) $argument] = true; $service = $this->container->getDefinition((string) $argument); $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); if ($this->hasReference($id, $arguments, $deep, $visited)) { return true; } } } } return false; } /** * Dumps values. * * @param array $value * @param Boolean $interpolate * * @return string * * @throws RuntimeException */ private function dumpValue($value, $interpolate = true) { if (is_array($value)) { $code = array(); foreach ($value as $k => $v) { $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); } return sprintf('array(%s)', implode(', ', $code)); } elseif ($value instanceof Definition) { if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate); } if (count($value->getMethodCalls()) > 0) { throw new RuntimeException('Cannot dump definitions which have method calls.'); } if (null !== $value->getConfigurator()) { throw new RuntimeException('Cannot dump definitions which have a configurator.'); } $arguments = array(); foreach ($value->getArguments() as $argument) { $arguments[] = $this->dumpValue($argument); } $class = $this->dumpValue($value->getClass()); if (false !== strpos($class, '$')) { throw new RuntimeException('Cannot dump definitions which have a variable class name.'); } if (null !== $value->getFactoryMethod()) { if (null !== $value->getFactoryClass()) { return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); } elseif (null !== $value->getFactoryService()) { return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments)); } else { throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.'); } } return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments)); } elseif ($value instanceof Variable) { return '$'.$value; } elseif ($value instanceof Reference) { if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) { return $this->dumpValue($this->referenceVariables[$id], $interpolate); } return $this->getServiceCall((string) $value, $value); } elseif ($value instanceof Parameter) { return $this->dumpParameter($value); } elseif (true === $interpolate && is_string($value)) { if (preg_match('/^%([^%]+)%$/', $value, $match)) { // we do this to deal with non string values (Boolean, integer, ...) // the preg_replace_callback converts them to strings return $this->dumpParameter(strtolower($match[1])); } else { $that = $this; $replaceParameters = function ($match) use ($that) { return "'.".$that->dumpParameter(strtolower($match[2])).".'"; }; $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, var_export($value, true))); return $code; } } elseif (is_object($value) || is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } else { return var_export($value, true); } } /** * Dumps a parameter * * @param string $name * * @return string */ public function dumpParameter($name) { if ($this->container->isFrozen() && $this->container->hasParameter($name)) { return $this->dumpValue($this->container->getParameter($name), false); } return sprintf("\$this->getParameter('%s')", strtolower($name)); } /** * Gets a service call * * @param string $id * @param Reference $reference * * @return string */ private function getServiceCall($id, Reference $reference = null) { if ('service_container' === $id) { return '$this'; } if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id); } else { if ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); } return sprintf('$this->get(\'%s\')', $id); } } /** * Returns the next name to use * * @return string */ private function getNextVariableName() { $firstChars = self::FIRST_CHARS; $firstCharsLength = strlen($firstChars); $nonFirstChars = self::NON_FIRST_CHARS; $nonFirstCharsLength = strlen($nonFirstChars); while (true) { $name = ''; $i = $this->variableCount; if ('' === $name) { $name .= $firstChars[$i%$firstCharsLength]; $i = intval($i/$firstCharsLength); } while ($i > 0) { $i -= 1; $name .= $nonFirstChars[$i%$nonFirstCharsLength]; $i = intval($i/$nonFirstCharsLength); } $this->variableCount += 1; // check that the name is not reserved if (in_array($name, $this->reservedVariables, true)) { continue; } return $name; } } } |