Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
PhpDumper.php
0001 <?php
0002
0003 /*
0004 * This file is part of the Symfony package.
0005 *
0006 * (c) Fabien Potencier <fabien@symfony.com>
0007 *
0008 * For the full copyright and license information, please view the LICENSE
0009 * file that was distributed with this source code.
0010 */
0011
0012 namespace Symfony\Component\DependencyInjection\Dumper;
0013
0014 use Symfony\Component\DependencyInjection\Variable;
0015 use Symfony\Component\DependencyInjection\Definition;
0016 use Symfony\Component\DependencyInjection\ContainerBuilder;
0017 use Symfony\Component\DependencyInjection\Container;
0018 use Symfony\Component\DependencyInjection\ContainerInterface;
0019 use Symfony\Component\DependencyInjection\Reference;
0020 use Symfony\Component\DependencyInjection\Parameter;
0021 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
0022 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
0023 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
0024 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
0025 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
0026
0027 /**
0028 * PhpDumper dumps a service container as a PHP class.
0029 *
0030 * @author Fabien Potencier <fabien@symfony.com>
0031 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
0032 *
0033 * @api
0034 */
0035 class PhpDumper extends Dumper
0036 {
0037 /**
0038 * Characters that might appear in the generated variable name as first character
0039 * @var string
0040 */
0041 const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
0042
0043 /**
0044 * Characters that might appear in the generated variable name as any but the first character
0045 * @var string
0046 */
0047 const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
0048
0049 private $inlinedDefinitions;
0050 private $definitionVariables;
0051 private $referenceVariables;
0052 private $variableCount;
0053 private $reservedVariables = array('instance', 'class');
0054
0055 /**
0056 * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
0057 */
0058 private $proxyDumper;
0059
0060 /**
0061 * {@inheritdoc}
0062 *
0063 * @api
0064 */
0065 public function __construct(ContainerBuilder $container)
0066 {
0067 parent::__construct($container);
0068
0069 $this->inlinedDefinitions = new \SplObjectStorage();
0070 }
0071
0072 /**
0073 * Sets the dumper to be used when dumping proxies in the generated container.
0074 *
0075 * @param ProxyDumper $proxyDumper
0076 */
0077 public function setProxyDumper(ProxyDumper $proxyDumper)
0078 {
0079 $this->proxyDumper = $proxyDumper;
0080 }
0081
0082 /**
0083 * Dumps the service container as a PHP class.
0084 *
0085 * Available options:
0086 *
0087 * * class: The class name
0088 * * base_class: The base class name
0089 *
0090 * @param array $options An array of options
0091 *
0092 * @return string A PHP class representing of the service container
0093 *
0094 * @api
0095 */
0096 public function dump(array $options = array())
0097 {
0098 $options = array_merge(array(
0099 'class' => 'ProjectServiceContainer',
0100 'base_class' => 'Container',
0101 ), $options);
0102
0103 $code = $this->startClass($options['class'], $options['base_class']);
0104
0105 if ($this->container->isFrozen()) {
0106 $code .= $this->addFrozenConstructor();
0107 } else {
0108 $code .= $this->addConstructor();
0109 }
0110
0111 $code .=
0112 $this->addServices().
0113 $this->addDefaultParametersMethod().
0114 $this->endClass().
0115 $this->addProxyClasses()
0116 ;
0117
0118 return $code;
0119 }
0120
0121 /**
0122 * Retrieves the currently set proxy dumper or instantiates one.
0123 *
0124 * @return ProxyDumper
0125 */
0126 private function getProxyDumper()
0127 {
0128 if (!$this->proxyDumper) {
0129 $this->proxyDumper = new NullDumper();
0130 }
0131
0132 return $this->proxyDumper;
0133 }
0134
0135 /**
0136 * Generates Service local temp variables.
0137 *
0138 * @param string $cId
0139 * @param string $definition
0140 *
0141 * @return string
0142 */
0143 private function addServiceLocalTempVariables($cId, $definition)
0144 {
0145 static $template = " \$%s = %s;\n";
0146
0147 $localDefinitions = array_merge(
0148 array($definition),
0149 $this->getInlinedDefinitions($definition)
0150 );
0151
0152 $calls = $behavior = array();
0153 foreach ($localDefinitions as $iDefinition) {
0154 $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
0155 $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
0156 $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
0157 }
0158
0159 $code = '';
0160 foreach ($calls as $id => $callCount) {
0161 if ('service_container' === $id || $id === $cId) {
0162 continue;
0163 }
0164
0165 if ($callCount > 1) {
0166 $name = $this->getNextVariableName();
0167 $this->referenceVariables[$id] = new Variable($name);
0168
0169 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
0170 $code .= sprintf($template, $name, $this->getServiceCall($id));
0171 } else {
0172 $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
0173 }
0174 }
0175 }
0176
0177 if ('' !== $code) {
0178 $code .= "\n";
0179 }
0180
0181 return $code;
0182 }
0183
0184 /**
0185 * Generates code for the proxies to be attached after the container class
0186 *
0187 * @return string
0188 */
0189 private function addProxyClasses()
0190 {
0191 /* @var $definitions Definition[] */
0192 $definitions = array_filter(
0193 $this->container->getDefinitions(),
0194 array($this->getProxyDumper(), 'isProxyCandidate')
0195 );
0196 $code = '';
0197
0198 foreach ($definitions as $definition) {
0199 $code .= "\n".$this->getProxyDumper()->getProxyCode($definition);
0200 }
0201
0202 return $code;
0203 }
0204
0205 /**
0206 * Generates the require_once statement for service includes.
0207 *
0208 * @param string $id The service id
0209 * @param Definition $definition
0210 *
0211 * @return string
0212 */
0213 private function addServiceInclude($id, $definition)
0214 {
0215 $template = " require_once %s;\n";
0216 $code = '';
0217
0218 if (null !== $file = $definition->getFile()) {
0219 $code .= sprintf($template, $this->dumpValue($file));
0220 }
0221
0222 foreach ($this->getInlinedDefinitions($definition) as $definition) {
0223 if (null !== $file = $definition->getFile()) {
0224 $code .= sprintf($template, $this->dumpValue($file));
0225 }
0226 }
0227
0228 if ('' !== $code) {
0229 $code .= "\n";
0230 }
0231
0232 return $code;
0233 }
0234
0235 /**
0236 * Generates the inline definition of a service.
0237 *
0238 * @param string $id
0239 * @param Definition $definition
0240 *
0241 * @return string
0242 *
0243 * @throws RuntimeException When the factory definition is incomplete
0244 * @throws ServiceCircularReferenceException When a circular reference is detected
0245 */
0246 private function addServiceInlinedDefinitions($id, $definition)
0247 {
0248 $code = '';
0249 $variableMap = $this->definitionVariables;
0250 $nbOccurrences = new \SplObjectStorage();
0251 $processed = new \SplObjectStorage();
0252 $inlinedDefinitions = $this->getInlinedDefinitions($definition);
0253
0254 foreach ($inlinedDefinitions as $definition) {
0255 if (false === $nbOccurrences->contains($definition)) {
0256 $nbOccurrences->offsetSet($definition, 1);
0257 } else {
0258 $i = $nbOccurrences->offsetGet($definition);
0259 $nbOccurrences->offsetSet($definition, $i + 1);
0260 }
0261 }
0262
0263 foreach ($inlinedDefinitions as $sDefinition) {
0264 if ($processed->contains($sDefinition)) {
0265 continue;
0266 }
0267 $processed->offsetSet($sDefinition);
0268
0269 $class = $this->dumpValue($sDefinition->getClass());
0270 if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
0271 $name = $this->getNextVariableName();
0272 $variableMap->offsetSet($sDefinition, new Variable($name));
0273
0274 // a construct like:
0275 // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
0276 // this is an indication for a wrong implementation, you can circumvent this problem
0277 // by setting up your service structure like this:
0278 // $b = new ServiceB();
0279 // $a = new ServiceA(ServiceB $b);
0280 // $b->setServiceA(ServiceA $a);
0281 if ($this->hasReference($id, $sDefinition->getArguments())) {
0282 throw new ServiceCircularReferenceException($id, array($id));
0283 }
0284
0285 $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
0286
0287 if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
0288 $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
0289 $code .= $this->addServiceProperties(null, $sDefinition, $name);
0290 $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
0291 }
0292
0293 $code .= "\n";
0294 }
0295 }
0296
0297 return $code;
0298 }
0299
0300 /**
0301 * Adds the service return statement.
0302 *
0303 * @param string $id Service id
0304 * @param Definition $definition
0305 *
0306 * @return string
0307 */
0308 private function addServiceReturn($id, $definition)
0309 {
0310 if ($this->isSimpleInstance($id, $definition)) {
0311 return " }\n";
0312 }
0313
0314 return "\n return \$instance;\n }\n";
0315 }
0316
0317 /**
0318 * Generates the service instance.
0319 *
0320 * @param string $id
0321 * @param Definition $definition
0322 *
0323 * @return string
0324 *
0325 * @throws InvalidArgumentException
0326 * @throws RuntimeException
0327 */
0328 private function addServiceInstance($id, $definition)
0329 {
0330 $class = $this->dumpValue($definition->getClass());
0331
0332 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)) {
0333 throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
0334 }
0335
0336 $simple = $this->isSimpleInstance($id, $definition);
0337 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
0338 $instantiation = '';
0339
0340 if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
0341 $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
0342 } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
0343 $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
0344 } elseif (!$simple) {
0345 $instantiation = '$instance';
0346 }
0347
0348 $return = '';
0349 if ($simple) {
0350 $return = 'return ';
0351 } else {
0352 $instantiation .= ' = ';
0353 }
0354
0355 $code = $this->addNewInstance($id, $definition, $return, $instantiation);
0356
0357 if (!$simple) {
0358 $code .= "\n";
0359 }
0360
0361 return $code;
0362 }
0363
0364 /**
0365 * Checks if the definition is a simple instance.
0366 *
0367 * @param string $id
0368 * @param Definition $definition
0369 *
0370 * @return bool
0371 */
0372 private function isSimpleInstance($id, $definition)
0373 {
0374 foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
0375 if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
0376 continue;
0377 }
0378
0379 if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
0380 return false;
0381 }
0382 }
0383
0384 return true;
0385 }
0386
0387 /**
0388 * Adds method calls to a service definition.
0389 *
0390 * @param string $id
0391 * @param Definition $definition
0392 * @param string $variableName
0393 *
0394 * @return string
0395 */
0396 private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
0397 {
0398 $calls = '';
0399 foreach ($definition->getMethodCalls() as $call) {
0400 $arguments = array();
0401 foreach ($call[1] as $value) {
0402 $arguments[] = $this->dumpValue($value);
0403 }
0404
0405 $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
0406 }
0407
0408 return $calls;
0409 }
0410
0411 private function addServiceProperties($id, $definition, $variableName = 'instance')
0412 {
0413 $code = '';
0414 foreach ($definition->getProperties() as $name => $value) {
0415 $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
0416 }
0417
0418 return $code;
0419 }
0420
0421 /**
0422 * Generates the inline definition setup.
0423 *
0424 * @param string $id
0425 * @param Definition $definition
0426 *
0427 * @return string
0428 *
0429 * @throws ServiceCircularReferenceException when the container contains a circular reference
0430 */
0431 private function addServiceInlinedDefinitionsSetup($id, $definition)
0432 {
0433 $this->referenceVariables[$id] = new Variable('instance');
0434
0435 $code = '';
0436 $processed = new \SplObjectStorage();
0437 foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
0438 if ($processed->contains($iDefinition)) {
0439 continue;
0440 }
0441 $processed->offsetSet($iDefinition);
0442
0443 if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
0444 continue;
0445 }
0446
0447 // if the instance is simple, the return statement has already been generated
0448 // so, the only possible way to get there is because of a circular reference
0449 if ($this->isSimpleInstance($id, $definition)) {
0450 throw new ServiceCircularReferenceException($id, array($id));
0451 }
0452
0453 $name = (string) $this->definitionVariables->offsetGet($iDefinition);
0454 $code .= $this->addServiceMethodCalls(null, $iDefinition, $name);
0455 $code .= $this->addServiceProperties(null, $iDefinition, $name);
0456 $code .= $this->addServiceConfigurator(null, $iDefinition, $name);
0457 }
0458
0459 if ('' !== $code) {
0460 $code .= "\n";
0461 }
0462
0463 return $code;
0464 }
0465
0466 /**
0467 * Adds configurator definition
0468 *
0469 * @param string $id
0470 * @param Definition $definition
0471 * @param string $variableName
0472 *
0473 * @return string
0474 */
0475 private function addServiceConfigurator($id, $definition, $variableName = 'instance')
0476 {
0477 if (!$callable = $definition->getConfigurator()) {
0478 return '';
0479 }
0480
0481 if (is_array($callable)) {
0482 if ($callable[0] instanceof Reference) {
0483 return sprintf(" %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $callable[1], $variableName);
0484 }
0485
0486 return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
0487 }
0488
0489 return sprintf(" %s(\$%s);\n", $callable, $variableName);
0490 }
0491
0492 /**
0493 * Adds a service
0494 *
0495 * @param string $id
0496 * @param Definition $definition
0497 *
0498 * @return string
0499 */
0500 private function addService($id, $definition)
0501 {
0502 $this->definitionVariables = new \SplObjectStorage();
0503 $this->referenceVariables = array();
0504 $this->variableCount = 0;
0505
0506 $return = array();
0507
0508 if ($definition->isSynthetic()) {
0509 $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
0510 } elseif ($class = $definition->getClass()) {
0511 $return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : "\\".$class, $class);
0512 } elseif ($definition->getFactoryClass()) {
0513 $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
0514 } elseif ($definition->getFactoryService()) {
0515 $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
0516 }
0517
0518 $scope = $definition->getScope();
0519 if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
0520 if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
0521 $return[] = '';
0522 }
0523 $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
0524 }
0525
0526 $return = implode("\n * ", $return);
0527
0528 $doc = '';
0529 if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
0530 $doc .= <<<EOF
0531
0532 *
0533 * This service is shared.
0534 * This method always returns the same instance of the service.
0535 EOF;
0536
0537 }
0538
0539 if (!$definition->isPublic()) {
0540 $doc .= <<<EOF
0541
0542 *
0543 * This service is private.
0544 * If you want to be able to request this service from the container directly,
0545 * make it public, otherwise you might end up with broken code.
0546 EOF;
0547
0548 }
0549
0550 if ($definition->isLazy()) {
0551 $lazyInitialization = '$lazyLoad = true';
0552 $lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *";
0553 } else {
0554 $lazyInitialization = '';
0555 $lazyInitializationDoc = '';
0556 }
0557
0558 // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
0559 $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
0560 $visibility = $isProxyCandidate ? 'public' : 'protected';
0561 $code = <<<EOF
0562
0563 /**
0564 * Gets the '$id' service.$doc
0565 *$lazyInitializationDoc
0566 * $return
0567 */
0568 {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
0569 {
0570
0571 EOF;
0572
0573
0574 $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
0575
0576 if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
0577 $code .= <<<EOF
0578 if (!isset(\$this->scopedServices['$scope'])) {
0579 throw new InactiveScopeException('$id', '$scope');
0580 }
0581
0582
0583 EOF;
0584
0585 }
0586
0587 if ($definition->isSynthetic()) {
0588 $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);
0589 } else {
0590 $code .=
0591 $this->addServiceInclude($id, $definition).
0592 $this->addServiceLocalTempVariables($id, $definition).
0593 $this->addServiceInlinedDefinitions($id, $definition).
0594 $this->addServiceInstance($id, $definition).
0595 $this->addServiceInlinedDefinitionsSetup($id, $definition).
0596 $this->addServiceMethodCalls($id, $definition).
0597 $this->addServiceProperties($id, $definition).
0598 $this->addServiceConfigurator($id, $definition).
0599 $this->addServiceReturn($id, $definition)
0600 ;
0601 }
0602
0603 $this->definitionVariables = null;
0604 $this->referenceVariables = null;
0605
0606 return $code;
0607 }
0608
0609 /**
0610 * Adds multiple services
0611 *
0612 * @return string
0613 */
0614 private function addServices()
0615 {
0616 $publicServices = $privateServices = $synchronizers = '';
0617 $definitions = $this->container->getDefinitions();
0618 ksort($definitions);
0619 foreach ($definitions as $id => $definition) {
0620 if ($definition->isPublic()) {
0621 $publicServices .= $this->addService($id, $definition);
0622 } else {
0623 $privateServices .= $this->addService($id, $definition);
0624 }
0625
0626 $synchronizers .= $this->addServiceSynchronizer($id, $definition);
0627 }
0628
0629 return $publicServices.$synchronizers.$privateServices;
0630 }
0631
0632 /**
0633 * Adds synchronizer methods.
0634 *
0635 * @param string $id A service identifier
0636 * @param Definition $definition A Definition instance
0637 *
0638 * @return string|null
0639 */
0640 private function addServiceSynchronizer($id, Definition $definition)
0641 {
0642 if (!$definition->isSynchronized()) {
0643 return;
0644 }
0645
0646 $code = '';
0647 foreach ($this->container->getDefinitions() as $definitionId => $definition) {
0648 foreach ($definition->getMethodCalls() as $call) {
0649 foreach ($call[1] as $argument) {
0650 if ($argument instanceof Reference && $id == (string) $argument) {
0651 $arguments = array();
0652 foreach ($call[1] as $value) {
0653 $arguments[] = $this->dumpValue($value);
0654 }
0655
0656 $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
0657
0658 $code .= <<<EOF
0659 if (\$this->initialized('$definitionId')) {
0660 $call
0661 }
0662
0663 EOF;
0664
0665 }
0666 }
0667 }
0668 }
0669
0670 if (!$code) {
0671 return;
0672 }
0673
0674 return <<<EOF
0675
0676 /**
0677 * Updates the '$id' service.
0678 */
0679 protected function synchronize{$this->camelize($id)}Service()
0680 {
0681 $code }
0682
0683 EOF;
0684
0685 }
0686
0687 private function addNewInstance($id, Definition $definition, $return, $instantiation)
0688 {
0689 $class = $this->dumpValue($definition->getClass());
0690
0691 $arguments = array();
0692 foreach ($definition->getArguments() as $value) {
0693 $arguments[] = $this->dumpValue($value);
0694 }
0695
0696 if (null !== $definition->getFactoryMethod()) {
0697 if (null !== $definition->getFactoryClass()) {
0698 return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
0699 }
0700
0701 if (null !== $definition->getFactoryService()) {
0702 return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
0703 }
0704
0705 throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
0706 }
0707
0708 if (false !== strpos($class, '$')) {
0709 return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
0710 }
0711
0712 return sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
0713 }
0714
0715 /**
0716 * Adds the class headers.
0717 *
0718 * @param string $class Class name
0719 * @param string $baseClass The name of the base class
0720 *
0721 * @return string
0722 */
0723 private function startClass($class, $baseClass)
0724 {
0725 $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
0726
0727 return <<<EOF
0728 <?php
0729
0730 use Symfony\Component\DependencyInjection\ContainerInterface;
0731 use Symfony\Component\DependencyInjection\Container;
0732 use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
0733 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
0734 use Symfony\Component\DependencyInjection\Exception\LogicException;
0735 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
0736 $bagClass
0737
0738 /**
0739 * $class
0740 *
0741 * This class has been auto-generated
0742 * by the Symfony Dependency Injection Component.
0743 */
0744 class $class extends $baseClass
0745 {
0746 EOF;
0747
0748 }
0749
0750 /**
0751 * Adds the constructor.
0752 *
0753 * @return string
0754 */
0755 private function addConstructor()
0756 {
0757 $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
0758
0759 $code = <<<EOF
0760
0761 /**
0762 * Constructor.
0763 */
0764 public function __construct()
0765 {
0766 parent::__construct($arguments);
0767
0768 EOF;
0769
0770
0771 if (count($scopes = $this->container->getScopes()) > 0) {
0772 $code .= "\n";
0773 $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n";
0774 $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
0775 }
0776
0777 $code .= $this->addMethodMap();
0778 $code .= $this->addAliases();
0779
0780 $code .= <<<EOF
0781 }
0782
0783 EOF;
0784
0785
0786 return $code;
0787 }
0788
0789 /**
0790 * Adds the constructor for a frozen container.
0791 *
0792 * @return string
0793 */
0794 private function addFrozenConstructor()
0795 {
0796 $code = <<<EOF
0797
0798 /**
0799 * Constructor.
0800 */
0801 public function __construct()
0802 {
0803 EOF;
0804
0805
0806 if ($this->container->getParameterBag()->all()) {
0807 $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
0808 }
0809
0810 $code .= <<<EOF
0811
0812 \$this->services =
0813 \$this->scopedServices =
0814 \$this->scopeStacks = array();
0815
0816 \$this->set('service_container', \$this);
0817
0818 EOF;
0819
0820
0821 $code .= "\n";
0822 if (count($scopes = $this->container->getScopes()) > 0) {
0823 $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n";
0824 $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
0825 } else {
0826 $code .= " \$this->scopes = array();\n";
0827 $code .= " \$this->scopeChildren = array();\n";
0828 }
0829
0830 $code .= $this->addMethodMap();
0831 $code .= $this->addAliases();
0832
0833 $code .= <<<EOF
0834 }
0835
0836 EOF;
0837
0838
0839 return $code;
0840 }
0841
0842 /**
0843 * Adds the methodMap property definition
0844 *
0845 * @return string
0846 */
0847 private function addMethodMap()
0848 {
0849 if (!$definitions = $this->container->getDefinitions()) {
0850 return '';
0851 }
0852
0853 $code = " \$this->methodMap = array(\n";
0854 ksort($definitions);
0855 foreach ($definitions as $id => $definition) {
0856 $code .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
0857 }
0858
0859 return $code." );\n";
0860 }
0861
0862 /**
0863 * Adds the aliases property definition
0864 *
0865 * @return string
0866 */
0867 private function addAliases()
0868 {
0869 if (!$aliases = $this->container->getAliases()) {
0870 if ($this->container->isFrozen()) {
0871 return "\n \$this->aliases = array();\n";
0872 } else {
0873 return '';
0874 }
0875 }
0876
0877 $code = " \$this->aliases = array(\n";
0878 ksort($aliases);
0879 foreach ($aliases as $alias => $id) {
0880 $id = (string) $id;
0881 while (isset($aliases[$id])) {
0882 $id = (string) $aliases[$id];
0883 }
0884 $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n";
0885 }
0886
0887 return $code." );\n";
0888 }
0889
0890 /**
0891 * Adds default parameters method.
0892 *
0893 * @return string
0894 */
0895 private function addDefaultParametersMethod()
0896 {
0897 if (!$this->container->getParameterBag()->all()) {
0898 return '';
0899 }
0900
0901 $parameters = $this->exportParameters($this->container->getParameterBag()->all());
0902
0903 $code = '';
0904 if ($this->container->isFrozen()) {
0905 $code .= <<<EOF
0906
0907 /**
0908 * {@inheritdoc}
0909 */
0910 public function getParameter(\$name)
0911 {
0912 \$name = strtolower(\$name);
0913
0914 if (!(isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) {
0915 throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
0916 }
0917
0918 return \$this->parameters[\$name];
0919 }
0920
0921 /**
0922 * {@inheritdoc}
0923 */
0924 public function hasParameter(\$name)
0925 {
0926 \$name = strtolower(\$name);
0927
0928 return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters);
0929 }
0930
0931 /**
0932 * {@inheritdoc}
0933 */
0934 public function setParameter(\$name, \$value)
0935 {
0936 throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
0937 }
0938
0939 /**
0940 * {@inheritdoc}
0941 */
0942 public function getParameterBag()
0943 {
0944 if (null === \$this->parameterBag) {
0945 \$this->parameterBag = new FrozenParameterBag(\$this->parameters);
0946 }
0947
0948 return \$this->parameterBag;
0949 }
0950 EOF;
0951
0952 }
0953
0954 $code .= <<<EOF
0955
0956 /**
0957 * Gets the default parameters.
0958 *
0959 * @return array An array of the default parameters
0960 */
0961 protected function getDefaultParameters()
0962 {
0963 return $parameters;
0964 }
0965
0966 EOF;
0967
0968
0969 return $code;
0970 }
0971
0972 /**
0973 * Exports parameters.
0974 *
0975 * @param array $parameters
0976 * @param string $path
0977 * @param int $indent
0978 *
0979 * @return string
0980 *
0981 * @throws InvalidArgumentException
0982 */
0983 private function exportParameters($parameters, $path = '', $indent = 12)
0984 {
0985 $php = array();
0986 foreach ($parameters as $key => $value) {
0987 if (is_array($value)) {
0988 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
0989 } elseif ($value instanceof Variable) {
0990 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
0991 } elseif ($value instanceof Definition) {
0992 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));
0993 } elseif ($value instanceof Reference) {
0994 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));
0995 } else {
0996 $value = var_export($value, true);
0997 }
0998
0999 $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
1000 }
1001
1002 return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
1003 }
1004
1005 /**
1006 * Ends the class definition.
1007 *
1008 * @return string
1009 */
1010 private function endClass()
1011 {
1012 return <<<EOF
1013 }
1014
1015 EOF;
1016
1017 }
1018
1019 /**
1020 * Wraps the service conditionals.
1021 *
1022 * @param string $value
1023 * @param string $code
1024 *
1025 * @return string
1026 */
1027 private function wrapServiceConditionals($value, $code)
1028 {
1029 if (!$services = ContainerBuilder::getServiceConditionals($value)) {
1030 return $code;
1031 }
1032
1033 $conditions = array();
1034 foreach ($services as $service) {
1035 $conditions[] = sprintf("\$this->has('%s')", $service);
1036 }
1037
1038 // re-indent the wrapped code
1039 $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
1040
1041 return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
1042 }
1043
1044 /**
1045 * Builds service calls from arguments.
1046 *
1047 * @param array $arguments
1048 * @param array &$calls By reference
1049 * @param array &$behavior By reference
1050 */
1051 private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
1052 {
1053 foreach ($arguments as $argument) {
1054 if (is_array($argument)) {
1055 $this->getServiceCallsFromArguments($argument, $calls, $behavior);
1056 } elseif ($argument instanceof Reference) {
1057 $id = (string) $argument;
1058
1059 if (!isset($calls[$id])) {
1060 $calls[$id] = 0;
1061 }
1062 if (!isset($behavior[$id])) {
1063 $behavior[$id] = $argument->getInvalidBehavior();
1064 } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
1065 $behavior[$id] = $argument->getInvalidBehavior();
1066 }
1067
1068 $calls[$id] += 1;
1069 }
1070 }
1071 }
1072
1073 /**
1074 * Returns the inline definition.
1075 *
1076 * @param Definition $definition
1077 *
1078 * @return array
1079 */
1080 private function getInlinedDefinitions(Definition $definition)
1081 {
1082 if (false === $this->inlinedDefinitions->contains($definition)) {
1083 $definitions = array_merge(
1084 $this->getDefinitionsFromArguments($definition->getArguments()),
1085 $this->getDefinitionsFromArguments($definition->getMethodCalls()),
1086 $this->getDefinitionsFromArguments($definition->getProperties())
1087 );
1088
1089 $this->inlinedDefinitions->offsetSet($definition, $definitions);
1090
1091 return $definitions;
1092 }
1093
1094 return $this->inlinedDefinitions->offsetGet($definition);
1095 }
1096
1097 /**
1098 * Gets the definition from arguments.
1099 *
1100 * @param array $arguments
1101 *
1102 * @return array
1103 */
1104 private function getDefinitionsFromArguments(array $arguments)
1105 {
1106 $definitions = array();
1107 foreach ($arguments as $argument) {
1108 if (is_array($argument)) {
1109 $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
1110 } elseif ($argument instanceof Definition) {
1111 $definitions = array_merge(
1112 $definitions,
1113 $this->getInlinedDefinitions($argument),
1114 array($argument)
1115 );
1116 }
1117 }
1118
1119 return $definitions;
1120 }
1121
1122 /**
1123 * Checks if a service id has a reference.
1124 *
1125 * @param string $id
1126 * @param array $arguments
1127 * @param bool $deep
1128 * @param array $visited
1129 *
1130 * @return bool
1131 */
1132 private function hasReference($id, array $arguments, $deep = false, $visited = array())
1133 {
1134 foreach ($arguments as $argument) {
1135 if (is_array($argument)) {
1136 if ($this->hasReference($id, $argument, $deep, $visited)) {
1137 return true;
1138 }
1139 } elseif ($argument instanceof Reference) {
1140 if ($id === (string) $argument) {
1141 return true;
1142 }
1143
1144 if ($deep && !isset($visited[(string) $argument])) {
1145 $visited[(string) $argument] = true;
1146
1147 $service = $this->container->getDefinition((string) $argument);
1148 $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
1149
1150 if ($this->hasReference($id, $arguments, $deep, $visited)) {
1151 return true;
1152 }
1153 }
1154 }
1155 }
1156
1157 return false;
1158 }
1159
1160 /**
1161 * Dumps values.
1162 *
1163 * @param array $value
1164 * @param bool $interpolate
1165 *
1166 * @return string
1167 *
1168 * @throws RuntimeException
1169 */
1170 private function dumpValue($value, $interpolate = true)
1171 {
1172 if (is_array($value)) {
1173 $code = array();
1174 foreach ($value as $k => $v) {
1175 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1176 }
1177
1178 return sprintf('array(%s)', implode(', ', $code));
1179 } elseif ($value instanceof Definition) {
1180 if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
1181 return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
1182 }
1183 if (count($value->getMethodCalls()) > 0) {
1184 throw new RuntimeException('Cannot dump definitions which have method calls.');
1185 }
1186 if (null !== $value->getConfigurator()) {
1187 throw new RuntimeException('Cannot dump definitions which have a configurator.');
1188 }
1189
1190 $arguments = array();
1191 foreach ($value->getArguments() as $argument) {
1192 $arguments[] = $this->dumpValue($argument);
1193 }
1194 $class = $this->dumpValue($value->getClass());
1195
1196 if (false !== strpos($class, '$')) {
1197 throw new RuntimeException('Cannot dump definitions which have a variable class name.');
1198 }
1199
1200 if (null !== $value->getFactoryMethod()) {
1201 if (null !== $value->getFactoryClass()) {
1202 return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
1203 } elseif (null !== $value->getFactoryService()) {
1204 return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments));
1205 } else {
1206 throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
1207 }
1208 }
1209
1210 return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
1211 } elseif ($value instanceof Variable) {
1212 return '$'.$value;
1213 } elseif ($value instanceof Reference) {
1214 if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
1215 return $this->dumpValue($this->referenceVariables[$id], $interpolate);
1216 }
1217
1218 return $this->getServiceCall((string) $value, $value);
1219 } elseif ($value instanceof Parameter) {
1220 return $this->dumpParameter($value);
1221 } elseif (true === $interpolate && is_string($value)) {
1222 if (preg_match('/^%([^%]+)%$/', $value, $match)) {
1223 // we do this to deal with non string values (Boolean, integer, ...)
1224 // the preg_replace_callback converts them to strings
1225 return $this->dumpParameter(strtolower($match[1]));
1226 } else {
1227 $that = $this;
1228 $replaceParameters = function ($match) use ($that) {
1229 return "'.".$that->dumpParameter(strtolower($match[2])).".'";
1230 };
1231
1232 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, var_export($value, true)));
1233
1234 return $code;
1235 }
1236 } elseif (is_object($value) || is_resource($value)) {
1237 throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
1238 } else {
1239 return var_export($value, true);
1240 }
1241 }
1242
1243 /**
1244 * Dumps a parameter
1245 *
1246 * @param string $name
1247 *
1248 * @return string
1249 */
1250 public function dumpParameter($name)
1251 {
1252 if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
1253 return $this->dumpValue($this->container->getParameter($name), false);
1254 }
1255
1256 return sprintf("\$this->getParameter('%s')", strtolower($name));
1257 }
1258
1259 /**
1260 * Gets a service call
1261 *
1262 * @param string $id
1263 * @param Reference $reference
1264 *
1265 * @return string
1266 */
1267 private function getServiceCall($id, Reference $reference = null)
1268 {
1269 if ('service_container' === $id) {
1270 return '$this';
1271 }
1272
1273 if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
1274 return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
1275 } else {
1276 if ($this->container->hasAlias($id)) {
1277 $id = (string) $this->container->getAlias($id);
1278 }
1279
1280 return sprintf('$this->get(\'%s\')', $id);
1281 }
1282 }
1283
1284 /**
1285 * Convert a service id to a valid PHP method name.
1286 *
1287 * @param string $id
1288 *
1289 * @return string
1290 *
1291 * @throws InvalidArgumentException
1292 */
1293 private function camelize($id)
1294 {
1295 $name = Container::camelize($id);
1296
1297 if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
1298 throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
1299 }
1300
1301 return $name;
1302 }
1303
1304 /**
1305 * Returns the next name to use
1306 *
1307 * @return string
1308 */
1309 private function getNextVariableName()
1310 {
1311 $firstChars = self::FIRST_CHARS;
1312 $firstCharsLength = strlen($firstChars);
1313 $nonFirstChars = self::NON_FIRST_CHARS;
1314 $nonFirstCharsLength = strlen($nonFirstChars);
1315
1316 while (true) {
1317 $name = '';
1318 $i = $this->variableCount;
1319
1320 if ('' === $name) {
1321 $name .= $firstChars[$i%$firstCharsLength];
1322 $i = intval($i/$firstCharsLength);
1323 }
1324
1325 while ($i > 0) {
1326 $i -= 1;
1327 $name .= $nonFirstChars[$i%$nonFirstCharsLength];
1328 $i = intval($i/$nonFirstCharsLength);
1329 }
1330
1331 $this->variableCount += 1;
1332
1333 // check that the name is not reserved
1334 if (in_array($name, $this->reservedVariables, true)) {
1335 continue;
1336 }
1337
1338 return $name;
1339 }
1340 }
1341 }
1342