[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Dumper/ -> PhpDumper.php (source)

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1