[ Index ]

PHP Cross Reference of phpBB-3.3.7-deutsch

title

Body

[close]

/vendor/symfony/dependency-injection/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\Argument\ArgumentInterface;
  15  use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
  16  use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  17  use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
  18  use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
  19  use Symfony\Component\DependencyInjection\Container;
  20  use Symfony\Component\DependencyInjection\ContainerBuilder;
  21  use Symfony\Component\DependencyInjection\ContainerInterface;
  22  use Symfony\Component\DependencyInjection\Definition;
  23  use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
  24  use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  25  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  26  use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  27  use Symfony\Component\DependencyInjection\ExpressionLanguage;
  28  use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
  29  use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
  30  use Symfony\Component\DependencyInjection\Parameter;
  31  use Symfony\Component\DependencyInjection\Reference;
  32  use Symfony\Component\DependencyInjection\TypedReference;
  33  use Symfony\Component\DependencyInjection\Variable;
  34  use Symfony\Component\ExpressionLanguage\Expression;
  35  use Symfony\Component\HttpKernel\Kernel;
  36  
  37  /**
  38   * PhpDumper dumps a service container as a PHP class.
  39   *
  40   * @author Fabien Potencier <fabien@symfony.com>
  41   * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  42   */
  43  class PhpDumper extends Dumper
  44  {
  45      /**
  46       * Characters that might appear in the generated variable name as first character.
  47       */
  48      const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
  49  
  50      /**
  51       * Characters that might appear in the generated variable name as any but the first character.
  52       */
  53      const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
  54  
  55      private $definitionVariables;
  56      private $referenceVariables;
  57      private $variableCount;
  58      private $inlinedDefinitions;
  59      private $serviceCalls;
  60      private $reservedVariables = ['instance', 'class', 'this'];
  61      private $expressionLanguage;
  62      private $targetDirRegex;
  63      private $targetDirMaxMatches;
  64      private $docStar;
  65      private $serviceIdToMethodNameMap;
  66      private $usedMethodNames;
  67      private $namespace;
  68      private $asFiles;
  69      private $hotPathTag;
  70      private $inlineRequires;
  71      private $inlinedRequires = [];
  72      private $circularReferences = [];
  73  
  74      /**
  75       * @var ProxyDumper
  76       */
  77      private $proxyDumper;
  78  
  79      /**
  80       * {@inheritdoc}
  81       */
  82      public function __construct(ContainerBuilder $container)
  83      {
  84          if (!$container->isCompiled()) {
  85              @trigger_error('Dumping an uncompiled ContainerBuilder is deprecated since Symfony 3.3 and will not be supported anymore in 4.0. Compile the container beforehand.', \E_USER_DEPRECATED);
  86          }
  87  
  88          parent::__construct($container);
  89      }
  90  
  91      /**
  92       * Sets the dumper to be used when dumping proxies in the generated container.
  93       */
  94      public function setProxyDumper(ProxyDumper $proxyDumper)
  95      {
  96          $this->proxyDumper = $proxyDumper;
  97      }
  98  
  99      /**
 100       * Dumps the service container as a PHP class.
 101       *
 102       * Available options:
 103       *
 104       *  * class:      The class name
 105       *  * base_class: The base class name
 106       *  * namespace:  The class namespace
 107       *  * as_files:   To split the container in several files
 108       *
 109       * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set
 110       *
 111       * @throws EnvParameterException When an env var exists but has not been dumped
 112       */
 113      public function dump(array $options = [])
 114      {
 115          $this->targetDirRegex = null;
 116          $this->inlinedRequires = [];
 117          $options = array_merge([
 118              'class' => 'ProjectServiceContainer',
 119              'base_class' => 'Container',
 120              'namespace' => '',
 121              'as_files' => false,
 122              'debug' => true,
 123              'hot_path_tag' => 'container.hot_path',
 124              'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
 125              'build_time' => time(),
 126          ], $options);
 127  
 128          $this->namespace = $options['namespace'];
 129          $this->asFiles = $options['as_files'];
 130          $this->hotPathTag = $options['hot_path_tag'];
 131          $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']);
 132  
 133          if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) {
 134              $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass);
 135              $baseClassWithNamespace = $baseClass;
 136          } elseif ('Container' === $baseClass) {
 137              $baseClassWithNamespace = Container::class;
 138          } else {
 139              $baseClassWithNamespace = $baseClass;
 140          }
 141  
 142          $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
 143  
 144          if ($this->getProxyDumper() instanceof NullDumper) {
 145              (new AnalyzeServiceReferencesPass(true, false))->process($this->container);
 146              try {
 147                  (new CheckCircularReferencesPass())->process($this->container);
 148              } catch (ServiceCircularReferenceException $e) {
 149                  $path = $e->getPath();
 150                  end($path);
 151                  $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge';
 152  
 153                  throw new ServiceCircularReferenceException($e->getServiceId(), $path);
 154              }
 155          }
 156  
 157          (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container);
 158          $checkedNodes = [];
 159          $this->circularReferences = [];
 160          foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
 161              if (!$node->getValue() instanceof Definition) {
 162                  continue;
 163              }
 164              if (!isset($checkedNodes[$id])) {
 165                  $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes);
 166              }
 167          }
 168          $this->container->getCompiler()->getServiceReferenceGraph()->clear();
 169          $checkedNodes = [];
 170  
 171          $this->docStar = $options['debug'] ? '*' : '';
 172  
 173          if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) {
 174              // Build a regexp where the first root dirs are mandatory,
 175              // but every other sub-dir is optional up to the full path in $dir
 176              // Mandate at least 1 root dir and not more than 5 optional dirs.
 177  
 178              $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir));
 179              $i = \count($dir);
 180  
 181              if (2 + (int) ('\\' === \DIRECTORY_SEPARATOR) <= $i) {
 182                  $regex = '';
 183                  $lastOptionalDir = $i > 8 ? $i - 5 : (2 + (int) ('\\' === \DIRECTORY_SEPARATOR));
 184                  $this->targetDirMaxMatches = $i - $lastOptionalDir;
 185  
 186                  while (--$i >= $lastOptionalDir) {
 187                      $regex = sprintf('(%s%s)?', preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex);
 188                  }
 189  
 190                  do {
 191                      $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex;
 192                  } while (0 < --$i);
 193  
 194                  $this->targetDirRegex = '#(^|file://|[:;, \|\r\n])'.preg_quote($dir[0], '#').$regex.'#';
 195              }
 196          }
 197  
 198          $code =
 199              $this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
 200              $this->addServices().
 201              $this->addDefaultParametersMethod().
 202              $this->endClass()
 203          ;
 204  
 205          if ($this->asFiles) {
 206              $fileStart = <<<EOF
 207  <?php
 208  
 209  use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
 210  
 211  // This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
 212  
 213  EOF;
 214              $files = [];
 215  
 216              if ($ids = array_keys($this->container->getRemovedIds())) {
 217                  sort($ids);
 218                  $c = "<?php\n\nreturn [\n";
 219                  foreach ($ids as $id) {
 220                      $c .= '    '.$this->doExport($id)." => true,\n";
 221                  }
 222                  $files['removed-ids.php'] = $c."];\n";
 223              }
 224  
 225              foreach ($this->generateServiceFiles() as $file => $c) {
 226                  $files[$file] = $fileStart.$c;
 227              }
 228              foreach ($this->generateProxyClasses() as $file => $c) {
 229                  $files[$file] = "<?php\n".$c;
 230              }
 231              $files[$options['class'].'.php'] = $code;
 232              $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx'));
 233              $code = [];
 234  
 235              foreach ($files as $file => $c) {
 236                  $code["Container{$hash}/{$file}"] = $c;
 237              }
 238              array_pop($code);
 239              $code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "<?php\n\nnamespace Container{$hash};\n", 0, 6);
 240              $namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
 241              $time = $options['build_time'];
 242              $id = hash('crc32', $hash.$time);
 243  
 244              $code[$options['class'].'.php'] = <<<EOF
 245  <?php
 246  {$namespaceLine}
 247  // This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
 248  
 249  if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) {
 250      // no-op
 251  } elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') {
 252      touch(__DIR__.'/Container{$hash}.legacy');
 253  
 254      return;
 255  }
 256  
 257  if (!\\class_exists({$options['class']}::class, false)) {
 258      \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false);
 259  }
 260  
 261  return new \\Container{$hash}\\{$options['class']}([
 262      'container.build_hash' => '$hash',
 263      'container.build_id' => '$id',
 264      'container.build_time' => $time,
 265  ], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}');
 266  
 267  EOF;
 268          } else {
 269              foreach ($this->generateProxyClasses() as $c) {
 270                  $code .= $c;
 271              }
 272          }
 273  
 274          $this->targetDirRegex = null;
 275          $this->inlinedRequires = [];
 276          $this->circularReferences = [];
 277  
 278          $unusedEnvs = [];
 279          foreach ($this->container->getEnvCounters() as $env => $use) {
 280              if (!$use) {
 281                  $unusedEnvs[] = $env;
 282              }
 283          }
 284          if ($unusedEnvs) {
 285              throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.');
 286          }
 287  
 288          return $code;
 289      }
 290  
 291      /**
 292       * Retrieves the currently set proxy dumper or instantiates one.
 293       *
 294       * @return ProxyDumper
 295       */
 296      private function getProxyDumper()
 297      {
 298          if (!$this->proxyDumper) {
 299              $this->proxyDumper = new NullDumper();
 300          }
 301  
 302          return $this->proxyDumper;
 303      }
 304  
 305      private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [], $byConstructor = true)
 306      {
 307          $checkedNodes[$sourceId] = true;
 308          $currentPath[$sourceId] = $byConstructor;
 309  
 310          foreach ($edges as $edge) {
 311              $node = $edge->getDestNode();
 312              $id = $node->getId();
 313  
 314              if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
 315                  // no-op
 316              } elseif (isset($currentPath[$id])) {
 317                  $this->addCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
 318              } elseif (!isset($checkedNodes[$id])) {
 319                  $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath, $edge->isReferencedByConstructor());
 320              } elseif (isset($this->circularReferences[$id])) {
 321                  $this->connectCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
 322              }
 323          }
 324          unset($currentPath[$sourceId]);
 325      }
 326  
 327      private function connectCircularReferences($sourceId, &$currentPath, $byConstructor, &$subPath = [])
 328      {
 329          $currentPath[$sourceId] = $subPath[$sourceId] = $byConstructor;
 330  
 331          foreach ($this->circularReferences[$sourceId] as $id => $byConstructor) {
 332              if (isset($currentPath[$id])) {
 333                  $this->addCircularReferences($id, $currentPath, $byConstructor);
 334              } elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
 335                  $this->connectCircularReferences($id, $currentPath, $byConstructor, $subPath);
 336              }
 337          }
 338          unset($currentPath[$sourceId], $subPath[$sourceId]);
 339      }
 340  
 341      private function addCircularReferences($id, $currentPath, $byConstructor)
 342      {
 343          $currentPath[$id] = $byConstructor;
 344          $circularRefs = [];
 345  
 346          foreach (array_reverse($currentPath) as $parentId => $v) {
 347              $byConstructor = $byConstructor && $v;
 348              $circularRefs[] = $parentId;
 349  
 350              if ($parentId === $id) {
 351                  break;
 352              }
 353          }
 354  
 355          $currentId = $id;
 356          foreach ($circularRefs as $parentId) {
 357              if (empty($this->circularReferences[$parentId][$currentId])) {
 358                  $this->circularReferences[$parentId][$currentId] = $byConstructor;
 359              }
 360  
 361              $currentId = $parentId;
 362          }
 363      }
 364  
 365      private function collectLineage($class, array &$lineage)
 366      {
 367          if (isset($lineage[$class])) {
 368              return;
 369          }
 370          if (!$r = $this->container->getReflectionClass($class, false)) {
 371              return;
 372          }
 373          if ($this->container instanceof $class) {
 374              return;
 375          }
 376          $file = $r->getFileName();
 377          if (') : eval()\'d code' === substr($file, -17)) {
 378              $file = substr($file, 0, strrpos($file, '(', -17));
 379          }
 380          if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) {
 381              return;
 382          }
 383  
 384          if ($parent = $r->getParentClass()) {
 385              $this->collectLineage($parent->name, $lineage);
 386          }
 387  
 388          foreach ($r->getInterfaces() as $parent) {
 389              $this->collectLineage($parent->name, $lineage);
 390          }
 391  
 392          foreach ($r->getTraits() as $parent) {
 393              $this->collectLineage($parent->name, $lineage);
 394          }
 395  
 396          $lineage[$class] = substr($exportedFile, 1, -1);
 397      }
 398  
 399      private function generateProxyClasses()
 400      {
 401          $alreadyGenerated = [];
 402          $definitions = $this->container->getDefinitions();
 403          $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
 404          $proxyDumper = $this->getProxyDumper();
 405          ksort($definitions);
 406          foreach ($definitions as $definition) {
 407              if (!$proxyDumper->isProxyCandidate($definition)) {
 408                  continue;
 409              }
 410              if (isset($alreadyGenerated[$class = $definition->getClass()])) {
 411                  continue;
 412              }
 413              $alreadyGenerated[$class] = true;
 414              // register class' reflector for resource tracking
 415              $this->container->getReflectionClass($class);
 416              if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) {
 417                  continue;
 418              }
 419              if ($strip) {
 420                  $proxyCode = "<?php\n".$proxyCode;
 421                  $proxyCode = substr(Kernel::stripComments($proxyCode), 5);
 422              }
 423              yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $proxyCode;
 424          }
 425      }
 426  
 427      /**
 428       * Generates the require_once statement for service includes.
 429       *
 430       * @return string
 431       */
 432      private function addServiceInclude($cId, Definition $definition)
 433      {
 434          $code = '';
 435  
 436          if ($this->inlineRequires && !$this->isHotPath($definition)) {
 437              $lineage = [];
 438              foreach ($this->inlinedDefinitions as $def) {
 439                  if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
 440                      $this->collectLineage($class, $lineage);
 441                  }
 442              }
 443  
 444              foreach ($this->serviceCalls as $id => list($callCount, $behavior)) {
 445                  if ('service_container' !== $id && $id !== $cId
 446                      && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
 447                      && $this->container->has($id)
 448                      && $this->isTrivialInstance($def = $this->container->findDefinition($id))
 449                      && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())
 450                  ) {
 451                      $this->collectLineage($class, $lineage);
 452                  }
 453              }
 454  
 455              foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
 456                  $code .= sprintf("        include_once %s;\n", $file);
 457              }
 458          }
 459  
 460          foreach ($this->inlinedDefinitions as $def) {
 461              if ($file = $def->getFile()) {
 462                  $code .= sprintf("        include_once %s;\n", $this->dumpValue($file));
 463              }
 464          }
 465  
 466          if ('' !== $code) {
 467              $code .= "\n";
 468          }
 469  
 470          return $code;
 471      }
 472  
 473      /**
 474       * Generates the service instance.
 475       *
 476       * @param string $id
 477       * @param bool   $isSimpleInstance
 478       *
 479       * @return string
 480       *
 481       * @throws InvalidArgumentException
 482       * @throws RuntimeException
 483       */
 484      private function addServiceInstance($id, Definition $definition, $isSimpleInstance)
 485      {
 486          $class = $this->dumpValue($definition->getClass());
 487  
 488          if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
 489              throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
 490          }
 491  
 492          $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
 493          $instantiation = '';
 494  
 495          if (!$isProxyCandidate && $definition->isShared()) {
 496              $instantiation = sprintf('$this->services[%s] = %s', $this->doExport($id), $isSimpleInstance ? '' : '$instance');
 497          } elseif (!$isSimpleInstance) {
 498              $instantiation = '$instance';
 499          }
 500  
 501          $return = '';
 502          if ($isSimpleInstance) {
 503              $return = 'return ';
 504          } else {
 505              $instantiation .= ' = ';
 506          }
 507  
 508          return $this->addNewInstance($definition, $return, $instantiation, $id);
 509      }
 510  
 511      /**
 512       * Checks if the definition is a trivial instance.
 513       *
 514       * @return bool
 515       */
 516      private function isTrivialInstance(Definition $definition)
 517      {
 518          if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) {
 519              return false;
 520          }
 521          if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < \count($definition->getArguments())) {
 522              return false;
 523          }
 524  
 525          foreach ($definition->getArguments() as $arg) {
 526              if (!$arg || $arg instanceof Parameter) {
 527                  continue;
 528              }
 529              if (\is_array($arg) && 3 >= \count($arg)) {
 530                  foreach ($arg as $k => $v) {
 531                      if ($this->dumpValue($k) !== $this->dumpValue($k, false)) {
 532                          return false;
 533                      }
 534                      if (!$v || $v instanceof Parameter) {
 535                          continue;
 536                      }
 537                      if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) {
 538                          continue;
 539                      }
 540                      if (!is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) {
 541                          return false;
 542                      }
 543                  }
 544              } elseif ($arg instanceof Reference && $this->container->has($id = (string) $arg) && $this->container->findDefinition($id)->isSynthetic()) {
 545                  continue;
 546              } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) {
 547                  return false;
 548              }
 549          }
 550  
 551          return true;
 552      }
 553  
 554      /**
 555       * Adds method calls to a service definition.
 556       *
 557       * @param string $variableName
 558       *
 559       * @return string
 560       */
 561      private function addServiceMethodCalls(Definition $definition, $variableName = 'instance')
 562      {
 563          $calls = '';
 564          foreach ($definition->getMethodCalls() as $call) {
 565              $arguments = [];
 566              foreach ($call[1] as $value) {
 567                  $arguments[] = $this->dumpValue($value);
 568              }
 569  
 570              $calls .= $this->wrapServiceConditionals($call[1], sprintf("        \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
 571          }
 572  
 573          return $calls;
 574      }
 575  
 576      private function addServiceProperties(Definition $definition, $variableName = 'instance')
 577      {
 578          $code = '';
 579          foreach ($definition->getProperties() as $name => $value) {
 580              $code .= sprintf("        \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
 581          }
 582  
 583          return $code;
 584      }
 585  
 586      /**
 587       * Adds configurator definition.
 588       *
 589       * @param string $variableName
 590       *
 591       * @return string
 592       */
 593      private function addServiceConfigurator(Definition $definition, $variableName = 'instance')
 594      {
 595          if (!$callable = $definition->getConfigurator()) {
 596              return '';
 597          }
 598  
 599          if (\is_array($callable)) {
 600              if ($callable[0] instanceof Reference
 601                  || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))
 602              ) {
 603                  return sprintf("        %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
 604              }
 605  
 606              $class = $this->dumpValue($callable[0]);
 607              // If the class is a string we can optimize call_user_func away
 608              if (0 === strpos($class, "'") && false === strpos($class, '$')) {
 609                  return sprintf("        %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName);
 610              }
 611  
 612              if (0 === strpos($class, 'new ')) {
 613                  return sprintf("        (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
 614              }
 615  
 616              return sprintf("        \\call_user_func([%s, '%s'], \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
 617          }
 618  
 619          return sprintf("        %s(\$%s);\n", $callable, $variableName);
 620      }
 621  
 622      /**
 623       * Adds a service.
 624       *
 625       * @param string $id
 626       * @param string &$file
 627       *
 628       * @return string
 629       */
 630      private function addService($id, Definition $definition, &$file = null)
 631      {
 632          $this->definitionVariables = new \SplObjectStorage();
 633          $this->referenceVariables = [];
 634          $this->variableCount = 0;
 635          $this->referenceVariables[$id] = new Variable('instance');
 636  
 637          $return = [];
 638  
 639          if ($class = $definition->getClass()) {
 640              $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
 641              $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\'));
 642          } elseif ($definition->getFactory()) {
 643              $factory = $definition->getFactory();
 644              if (\is_string($factory)) {
 645                  $return[] = sprintf('@return object An instance returned by %s()', $factory);
 646              } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
 647                  $class = $factory[0] instanceof Definition ? $factory[0]->getClass() : (string) $factory[0];
 648                  $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
 649                  $return[] = sprintf('@return object An instance returned by %s::%s()', $class, $factory[1]);
 650              }
 651          }
 652  
 653          if ($definition->isDeprecated()) {
 654              if ($return && 0 === strpos($return[\count($return) - 1], '@return')) {
 655                  $return[] = '';
 656              }
 657  
 658              $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id));
 659          }
 660  
 661          $return = str_replace("\n     * \n", "\n     *\n", implode("\n     * ", $return));
 662          $return = $this->container->resolveEnvPlaceholders($return);
 663  
 664          $shared = $definition->isShared() ? ' shared' : '';
 665          $public = $definition->isPublic() ? 'public' : 'private';
 666          $autowired = $definition->isAutowired() ? ' autowired' : '';
 667  
 668          if ($definition->isLazy()) {
 669              $lazyInitialization = '$lazyLoad = true';
 670          } else {
 671              $lazyInitialization = '';
 672          }
 673  
 674          $asFile = $this->asFiles && $definition->isShared() && !$this->isHotPath($definition);
 675          $methodName = $this->generateMethodName($id);
 676          if ($asFile) {
 677              $file = $methodName.'.php';
 678              $code = "        // Returns the $public '$id'$shared$autowired service.\n\n";
 679          } else {
 680              $code = <<<EOF
 681  
 682      /*{$this->docStar}
 683       * Gets the $public '$id'$shared$autowired service.
 684       *
 685       * $return
 686  EOF;
 687              $code = str_replace('*/', ' ', $code).<<<EOF
 688  
 689       */
 690      protected function {$methodName}($lazyInitialization)
 691      {
 692  
 693  EOF;
 694          }
 695  
 696          $this->serviceCalls = [];
 697          $this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls);
 698  
 699          $code .= $this->addServiceInclude($id, $definition);
 700  
 701          if ($this->getProxyDumper()->isProxyCandidate($definition)) {
 702              $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)';
 703              $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->doExport($id)));
 704          }
 705  
 706          if ($definition->isDeprecated()) {
 707              $code .= sprintf("        @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
 708          }
 709  
 710          $code .= $this->addInlineService($id, $definition);
 711  
 712          if ($asFile) {
 713              $code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
 714          } else {
 715              $code .= "    }\n";
 716          }
 717  
 718          $this->definitionVariables = $this->inlinedDefinitions = null;
 719          $this->referenceVariables = $this->serviceCalls = null;
 720  
 721          return $code;
 722      }
 723  
 724      private function addInlineVariables($id, Definition $definition, array $arguments, $forConstructor)
 725      {
 726          $code = '';
 727  
 728          foreach ($arguments as $argument) {
 729              if (\is_array($argument)) {
 730                  $code .= $this->addInlineVariables($id, $definition, $argument, $forConstructor);
 731              } elseif ($argument instanceof Reference) {
 732                  $code .= $this->addInlineReference($id, $definition, $this->container->normalizeId($argument), $forConstructor);
 733              } elseif ($argument instanceof Definition) {
 734                  $code .= $this->addInlineService($id, $definition, $argument, $forConstructor);
 735              }
 736          }
 737  
 738          return $code;
 739      }
 740  
 741      private function addInlineReference($id, Definition $definition, $targetId, $forConstructor)
 742      {
 743          while ($this->container->hasAlias($targetId)) {
 744              $targetId = (string) $this->container->getAlias($targetId);
 745          }
 746  
 747          list($callCount, $behavior) = $this->serviceCalls[$targetId];
 748  
 749          if ($id === $targetId) {
 750              return $this->addInlineService($id, $definition, $definition);
 751          }
 752  
 753          if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
 754              return '';
 755          }
 756  
 757          $hasSelfRef = isset($this->circularReferences[$id][$targetId]) && !isset($this->definitionVariables[$definition]);
 758  
 759          if ($hasSelfRef && !$forConstructor && !$forConstructor = !$this->circularReferences[$id][$targetId]) {
 760              $code = $this->addInlineService($id, $definition, $definition);
 761          } else {
 762              $code = '';
 763          }
 764  
 765          if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
 766              return $code;
 767          }
 768  
 769          $name = $this->getNextVariableName();
 770          $this->referenceVariables[$targetId] = new Variable($name);
 771  
 772          $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null;
 773          $code .= sprintf("        \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference));
 774  
 775          if (!$hasSelfRef || !$forConstructor) {
 776              return $code;
 777          }
 778  
 779          $code .= sprintf(<<<'EOTXT'
 780  
 781          if (isset($this->%s[%s])) {
 782              return $this->%1$s[%2$s];
 783          }
 784  
 785  EOTXT
 786              ,
 787              'services',
 788              $this->doExport($id)
 789          );
 790  
 791          return $code;
 792      }
 793  
 794      private function addInlineService($id, Definition $definition, Definition $inlineDef = null, $forConstructor = true)
 795      {
 796          $code = '';
 797  
 798          if ($isSimpleInstance = $isRootInstance = null === $inlineDef) {
 799              foreach ($this->serviceCalls as $targetId => list($callCount, $behavior, $byConstructor)) {
 800                  if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId]) {
 801                      $code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor);
 802                  }
 803              }
 804          }
 805  
 806          if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) {
 807              return $code;
 808          }
 809  
 810          $arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()];
 811  
 812          $code .= $this->addInlineVariables($id, $definition, $arguments, $forConstructor);
 813  
 814          if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) {
 815              $isSimpleInstance = false;
 816          } elseif ($definition !== $inlineDef && 2 > $this->inlinedDefinitions[$inlineDef]) {
 817              return $code;
 818          }
 819  
 820          if (isset($this->definitionVariables[$inlineDef])) {
 821              $isSimpleInstance = false;
 822          } else {
 823              $name = $definition === $inlineDef ? 'instance' : $this->getNextVariableName();
 824              $this->definitionVariables[$inlineDef] = new Variable($name);
 825              $code .= '' !== $code ? "\n" : '';
 826  
 827              if ('instance' === $name) {
 828                  $code .= $this->addServiceInstance($id, $definition, $isSimpleInstance);
 829              } else {
 830                  $code .= $this->addNewInstance($inlineDef, '$'.$name, ' = ', $id);
 831              }
 832  
 833              if ('' !== $inline = $this->addInlineVariables($id, $definition, $arguments, false)) {
 834                  $code .= "\n".$inline."\n";
 835              } elseif ($arguments && 'instance' === $name) {
 836                  $code .= "\n";
 837              }
 838  
 839              $code .= $this->addServiceProperties($inlineDef, $name);
 840              $code .= $this->addServiceMethodCalls($inlineDef, $name);
 841              $code .= $this->addServiceConfigurator($inlineDef, $name);
 842          }
 843  
 844          if ($isRootInstance && !$isSimpleInstance) {
 845              $code .= "\n        return \$instance;\n";
 846          }
 847  
 848          return $code;
 849      }
 850  
 851      /**
 852       * Adds multiple services.
 853       *
 854       * @return string
 855       */
 856      private function addServices()
 857      {
 858          $publicServices = $privateServices = '';
 859          $definitions = $this->container->getDefinitions();
 860          ksort($definitions);
 861          foreach ($definitions as $id => $definition) {
 862              if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition))) {
 863                  continue;
 864              }
 865              if ($definition->isPublic()) {
 866                  $publicServices .= $this->addService($id, $definition);
 867              } else {
 868                  $privateServices .= $this->addService($id, $definition);
 869              }
 870          }
 871  
 872          return $publicServices.$privateServices;
 873      }
 874  
 875      private function generateServiceFiles()
 876      {
 877          $definitions = $this->container->getDefinitions();
 878          ksort($definitions);
 879          foreach ($definitions as $id => $definition) {
 880              if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) {
 881                  $code = $this->addService($id, $definition, $file);
 882                  yield $file => $code;
 883              }
 884          }
 885      }
 886  
 887      private function addNewInstance(Definition $definition, $return, $instantiation, $id)
 888      {
 889          $class = $this->dumpValue($definition->getClass());
 890          $return = '        '.$return.$instantiation;
 891  
 892          $arguments = [];
 893          foreach ($definition->getArguments() as $value) {
 894              $arguments[] = $this->dumpValue($value);
 895          }
 896  
 897          if (null !== $definition->getFactory()) {
 898              $callable = $definition->getFactory();
 899              if (\is_array($callable)) {
 900                  if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) {
 901                      throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $callable[1] ?: 'n/a'));
 902                  }
 903  
 904                  if ($callable[0] instanceof Reference
 905                      || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
 906                      return $return.sprintf("%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
 907                  }
 908  
 909                  $class = $this->dumpValue($callable[0]);
 910                  // If the class is a string we can optimize call_user_func away
 911                  if (0 === strpos($class, "'") && false === strpos($class, '$')) {
 912                      if ("''" === $class) {
 913                          throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id));
 914                      }
 915  
 916                      return $return.sprintf("%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
 917                  }
 918  
 919                  if (0 === strpos($class, 'new ')) {
 920                      return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : '');
 921                  }
 922  
 923                  return $return.sprintf("\\call_user_func([%s, '%s']%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
 924              }
 925  
 926              return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '');
 927          }
 928  
 929          if (false !== strpos($class, '$')) {
 930              return sprintf("        \$class = %s;\n\n%snew \$class(%s);\n", $class, $return, implode(', ', $arguments));
 931          }
 932  
 933          return $return.sprintf("new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments));
 934      }
 935  
 936      /**
 937       * Adds the class headers.
 938       *
 939       * @param string $class                  Class name
 940       * @param string $baseClass              The name of the base class
 941       * @param string $baseClassWithNamespace Fully qualified base class name
 942       *
 943       * @return string
 944       */
 945      private function startClass($class, $baseClass, $baseClassWithNamespace)
 946      {
 947          $bagClass = $this->container->isCompiled() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
 948          $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
 949  
 950          $code = <<<EOF
 951  <?php
 952  $namespaceLine
 953  use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
 954  use Symfony\Component\DependencyInjection\ContainerInterface;
 955  use Symfony\Component\DependencyInjection\Container;
 956  use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 957  use Symfony\Component\DependencyInjection\Exception\LogicException;
 958  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 959  $bagClass
 960  
 961  /*{$this->docStar}
 962   * This class has been auto-generated
 963   * by the Symfony Dependency Injection Component.
 964   *
 965   * @final since Symfony 3.3
 966   */
 967  class $class extends $baseClass
 968  {
 969      private \$parameters = [];
 970      private \$targetDirs = [];
 971  
 972      public function __construct()
 973      {
 974  
 975  EOF;
 976          if (null !== $this->targetDirRegex) {
 977              $dir = $this->asFiles ? '$this->targetDirs[0] = \\dirname($containerDir)' : '__DIR__';
 978              $code .= <<<EOF
 979          \$dir = {$dir};
 980          for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
 981              \$this->targetDirs[\$i] = \$dir = \\dirname(\$dir);
 982          }
 983  
 984  EOF;
 985          }
 986          if ($this->asFiles) {
 987              $code = str_replace('$parameters', "\$buildParameters;\n    private \$containerDir;\n    private \$parameters", $code);
 988              $code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code);
 989              $code .= "        \$this->buildParameters = \$buildParameters;\n";
 990              $code .= "        \$this->containerDir = \$containerDir;\n";
 991          }
 992  
 993          if ($this->container->isCompiled()) {
 994              if (Container::class !== $baseClassWithNamespace) {
 995                  $r = $this->container->getReflectionClass($baseClassWithNamespace, false);
 996                  if (null !== $r
 997                      && (null !== $constructor = $r->getConstructor())
 998                      && 0 === $constructor->getNumberOfRequiredParameters()
 999                      && Container::class !== $constructor->getDeclaringClass()->name
1000                  ) {
1001                      $code .= "        parent::__construct();\n";
1002                      $code .= "        \$this->parameterBag = null;\n\n";
1003                  }
1004              }
1005  
1006              if ($this->container->getParameterBag()->all()) {
1007                  $code .= "        \$this->parameters = \$this->getDefaultParameters();\n\n";
1008              }
1009  
1010              $code .= "        \$this->services = [];\n";
1011          } else {
1012              $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
1013              $code .= "        parent::__construct($arguments);\n";
1014          }
1015  
1016          $code .= $this->addNormalizedIds();
1017          $code .= $this->addSyntheticIds();
1018          $code .= $this->addMethodMap();
1019          $code .= $this->asFiles ? $this->addFileMap() : '';
1020          $code .= $this->addPrivateServices();
1021          $code .= $this->addAliases();
1022          $code .= $this->addInlineRequires();
1023          $code .= <<<'EOF'
1024      }
1025  
1026  EOF;
1027          $code .= $this->addRemovedIds();
1028  
1029          if ($this->container->isCompiled()) {
1030              $code .= <<<EOF
1031  
1032      public function compile()
1033      {
1034          throw new LogicException('You cannot compile a dumped container that was already compiled.');
1035      }
1036  
1037      public function isCompiled()
1038      {
1039          return true;
1040      }
1041  
1042      public function isFrozen()
1043      {
1044          @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
1045  
1046          return true;
1047      }
1048  
1049  EOF;
1050          }
1051  
1052          if ($this->asFiles) {
1053              $code .= <<<EOF
1054  
1055      protected function load(\$file, \$lazyLoad = true)
1056      {
1057          return require \$this->containerDir.\\DIRECTORY_SEPARATOR.\$file;
1058      }
1059  
1060  EOF;
1061          }
1062  
1063          $proxyDumper = $this->getProxyDumper();
1064          foreach ($this->container->getDefinitions() as $definition) {
1065              if (!$proxyDumper->isProxyCandidate($definition)) {
1066                  continue;
1067              }
1068              if ($this->asFiles) {
1069                  $proxyLoader = '$this->load("{$class}.php")';
1070              } elseif ($this->namespace) {
1071                  $proxyLoader = 'class_alias("'.$this->namespace.'\\\\{$class}", $class, false)';
1072              } else {
1073                  $proxyLoader = '';
1074              }
1075              if ($proxyLoader) {
1076                  $proxyLoader = "class_exists(\$class, false) || {$proxyLoader};\n\n        ";
1077              }
1078              $code .= <<<EOF
1079  
1080      protected function createProxy(\$class, \Closure \$factory)
1081      {
1082          {$proxyLoader}return \$factory();
1083      }
1084  
1085  EOF;
1086              break;
1087          }
1088  
1089          return $code;
1090      }
1091  
1092      /**
1093       * Adds the normalizedIds property definition.
1094       *
1095       * @return string
1096       */
1097      private function addNormalizedIds()
1098      {
1099          $code = '';
1100          $normalizedIds = $this->container->getNormalizedIds();
1101          ksort($normalizedIds);
1102          foreach ($normalizedIds as $id => $normalizedId) {
1103              if ($this->container->has($normalizedId)) {
1104                  $code .= '            '.$this->doExport($id).' => '.$this->doExport($normalizedId).",\n";
1105              }
1106          }
1107  
1108          return $code ? "        \$this->normalizedIds = [\n".$code."        ];\n" : '';
1109      }
1110  
1111      /**
1112       * Adds the syntheticIds definition.
1113       *
1114       * @return string
1115       */
1116      private function addSyntheticIds()
1117      {
1118          $code = '';
1119          $definitions = $this->container->getDefinitions();
1120          ksort($definitions);
1121          foreach ($definitions as $id => $definition) {
1122              if ($definition->isSynthetic() && 'service_container' !== $id) {
1123                  $code .= '            '.$this->doExport($id)." => true,\n";
1124              }
1125          }
1126  
1127          return $code ? "        \$this->syntheticIds = [\n{$code}        ];\n" : '';
1128      }
1129  
1130      /**
1131       * Adds the removedIds definition.
1132       *
1133       * @return string
1134       */
1135      private function addRemovedIds()
1136      {
1137          if (!$ids = $this->container->getRemovedIds()) {
1138              return '';
1139          }
1140          if ($this->asFiles) {
1141              $code = "require \$this->containerDir.\\DIRECTORY_SEPARATOR.'removed-ids.php'";
1142          } else {
1143              $code = '';
1144              $ids = array_keys($ids);
1145              sort($ids);
1146              foreach ($ids as $id) {
1147                  if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id)) {
1148                      continue;
1149                  }
1150                  $code .= '            '.$this->doExport($id)." => true,\n";
1151              }
1152  
1153              $code = "[\n{$code}        ]";
1154          }
1155  
1156          return <<<EOF
1157  
1158      public function getRemovedIds()
1159      {
1160          return {$code};
1161      }
1162  
1163  EOF;
1164      }
1165  
1166      /**
1167       * Adds the methodMap property definition.
1168       *
1169       * @return string
1170       */
1171      private function addMethodMap()
1172      {
1173          $code = '';
1174          $definitions = $this->container->getDefinitions();
1175          ksort($definitions);
1176          foreach ($definitions as $id => $definition) {
1177              if (!$definition->isSynthetic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) {
1178                  $code .= '            '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n";
1179              }
1180          }
1181  
1182          return $code ? "        \$this->methodMap = [\n{$code}        ];\n" : '';
1183      }
1184  
1185      /**
1186       * Adds the fileMap property definition.
1187       *
1188       * @return string
1189       */
1190      private function addFileMap()
1191      {
1192          $code = '';
1193          $definitions = $this->container->getDefinitions();
1194          ksort($definitions);
1195          foreach ($definitions as $id => $definition) {
1196              if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) {
1197                  $code .= sprintf("            %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id));
1198              }
1199          }
1200  
1201          return $code ? "        \$this->fileMap = [\n{$code}        ];\n" : '';
1202      }
1203  
1204      /**
1205       * Adds the privates property definition.
1206       *
1207       * @return string
1208       */
1209      private function addPrivateServices()
1210      {
1211          $code = '';
1212  
1213          $aliases = $this->container->getAliases();
1214          ksort($aliases);
1215          foreach ($aliases as $id => $alias) {
1216              if ($alias->isPrivate()) {
1217                  $code .= '            '.$this->doExport($id)." => true,\n";
1218              }
1219          }
1220  
1221          $definitions = $this->container->getDefinitions();
1222          ksort($definitions);
1223          foreach ($definitions as $id => $definition) {
1224              if (!$definition->isPublic()) {
1225                  $code .= '            '.$this->doExport($id)." => true,\n";
1226              }
1227          }
1228  
1229          if (empty($code)) {
1230              return '';
1231          }
1232  
1233          $out = "        \$this->privates = [\n";
1234          $out .= $code;
1235          $out .= "        ];\n";
1236  
1237          return $out;
1238      }
1239  
1240      /**
1241       * Adds the aliases property definition.
1242       *
1243       * @return string
1244       */
1245      private function addAliases()
1246      {
1247          if (!$aliases = $this->container->getAliases()) {
1248              return $this->container->isCompiled() ? "\n        \$this->aliases = [];\n" : '';
1249          }
1250  
1251          $code = "        \$this->aliases = [\n";
1252          ksort($aliases);
1253          foreach ($aliases as $alias => $id) {
1254              $id = $this->container->normalizeId($id);
1255              while (isset($aliases[$id])) {
1256                  $id = $this->container->normalizeId($aliases[$id]);
1257              }
1258              $code .= '            '.$this->doExport($alias).' => '.$this->doExport($id).",\n";
1259          }
1260  
1261          return $code."        ];\n";
1262      }
1263  
1264      private function addInlineRequires()
1265      {
1266          if (!$this->hotPathTag || !$this->inlineRequires) {
1267              return '';
1268          }
1269  
1270          $lineage = [];
1271  
1272          foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) {
1273              $definition = $this->container->getDefinition($id);
1274              $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);
1275  
1276              foreach ($inlinedDefinitions as $def) {
1277                  if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
1278                      $this->collectLineage($class, $lineage);
1279                  }
1280              }
1281          }
1282  
1283          $code = '';
1284  
1285          foreach ($lineage as $file) {
1286              if (!isset($this->inlinedRequires[$file])) {
1287                  $this->inlinedRequires[$file] = true;
1288                  $code .= sprintf("\n            include_once %s;", $file);
1289              }
1290          }
1291  
1292          return $code ? sprintf("\n        \$this->privates['service_container'] = function () {%s\n        };\n", $code) : '';
1293      }
1294  
1295      /**
1296       * Adds default parameters method.
1297       *
1298       * @return string
1299       */
1300      private function addDefaultParametersMethod()
1301      {
1302          if (!$this->container->getParameterBag()->all()) {
1303              return '';
1304          }
1305  
1306          $php = [];
1307          $dynamicPhp = [];
1308          $normalizedParams = [];
1309  
1310          foreach ($this->container->getParameterBag()->all() as $key => $value) {
1311              if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) {
1312                  throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: "%s".', $resolvedKey));
1313              }
1314              if ($key !== $lcKey = strtolower($key)) {
1315                  $normalizedParams[] = sprintf('        %s => %s,', $this->export($lcKey), $this->export($key));
1316              }
1317              $export = $this->exportParameters([$value]);
1318              $export = explode('0 => ', substr(rtrim($export, " ]\n"), 2, -1), 2);
1319  
1320              if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) {
1321                  $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]);
1322              } else {
1323                  $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
1324              }
1325          }
1326  
1327          $parameters = sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', 8));
1328  
1329          $code = '';
1330          if ($this->container->isCompiled()) {
1331              $code .= <<<'EOF'
1332  
1333      public function getParameter($name)
1334      {
1335          $name = (string) $name;
1336          if (isset($this->buildParameters[$name])) {
1337              return $this->buildParameters[$name];
1338          }
1339          if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
1340              $name = $this->normalizeParameterName($name);
1341  
1342              if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
1343                  throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
1344              }
1345          }
1346          if (isset($this->loadedDynamicParameters[$name])) {
1347              return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
1348          }
1349  
1350          return $this->parameters[$name];
1351      }
1352  
1353      public function hasParameter($name)
1354      {
1355          $name = (string) $name;
1356          if (isset($this->buildParameters[$name])) {
1357              return true;
1358          }
1359          $name = $this->normalizeParameterName($name);
1360  
1361          return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
1362      }
1363  
1364      public function setParameter($name, $value)
1365      {
1366          throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
1367      }
1368  
1369      public function getParameterBag()
1370      {
1371          if (null === $this->parameterBag) {
1372              $parameters = $this->parameters;
1373              foreach ($this->loadedDynamicParameters as $name => $loaded) {
1374                  $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
1375              }
1376              foreach ($this->buildParameters as $name => $value) {
1377                  $parameters[$name] = $value;
1378              }
1379              $this->parameterBag = new FrozenParameterBag($parameters);
1380          }
1381  
1382          return $this->parameterBag;
1383      }
1384  
1385  EOF;
1386              if (!$this->asFiles) {
1387                  $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code);
1388              }
1389  
1390              if ($dynamicPhp) {
1391                  $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8);
1392                  $getDynamicParameter = <<<'EOF'
1393          switch ($name) {
1394  %s
1395              default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name));
1396          }
1397          $this->loadedDynamicParameters[$name] = true;
1398  
1399          return $this->dynamicParameters[$name] = $value;
1400  EOF;
1401                  $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp));
1402              } else {
1403                  $loadedDynamicParameters = '[]';
1404                  $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));';
1405              }
1406  
1407              $code .= <<<EOF
1408  
1409      private \$loadedDynamicParameters = {$loadedDynamicParameters};
1410      private \$dynamicParameters = [];
1411  
1412      /*{$this->docStar}
1413       * Computes a dynamic parameter.
1414       *
1415       * @param string \$name The name of the dynamic parameter to load
1416       *
1417       * @return mixed The value of the dynamic parameter
1418       *
1419       * @throws InvalidArgumentException When the dynamic parameter does not exist
1420       */
1421      private function getDynamicParameter(\$name)
1422      {
1423  {$getDynamicParameter}
1424      }
1425  
1426  
1427  EOF;
1428  
1429              $code .= '    private $normalizedParameterNames = '.($normalizedParams ? sprintf("[\n%s\n    ];", implode("\n", $normalizedParams)) : '[];')."\n";
1430              $code .= <<<'EOF'
1431  
1432      private function normalizeParameterName($name)
1433      {
1434          if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) {
1435              $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName;
1436              if ((string) $name !== $normalizedName) {
1437                  @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED);
1438              }
1439          } else {
1440              $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name;
1441          }
1442  
1443          return $normalizedName;
1444      }
1445  
1446  EOF;
1447          } elseif ($dynamicPhp) {
1448              throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.');
1449          }
1450  
1451          $code .= <<<EOF
1452  
1453      /*{$this->docStar}
1454       * Gets the default parameters.
1455       *
1456       * @return array An array of the default parameters
1457       */
1458      protected function getDefaultParameters()
1459      {
1460          return $parameters;
1461      }
1462  
1463  EOF;
1464  
1465          return $code;
1466      }
1467  
1468      /**
1469       * Exports parameters.
1470       *
1471       * @param string $path
1472       * @param int    $indent
1473       *
1474       * @return string
1475       *
1476       * @throws InvalidArgumentException
1477       */
1478      private function exportParameters(array $parameters, $path = '', $indent = 12)
1479      {
1480          $php = [];
1481          foreach ($parameters as $key => $value) {
1482              if (\is_array($value)) {
1483                  $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
1484              } elseif ($value instanceof ArgumentInterface) {
1485                  throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', \get_class($value), $path.'/'.$key));
1486              } elseif ($value instanceof Variable) {
1487                  throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
1488              } elseif ($value instanceof Definition) {
1489                  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));
1490              } elseif ($value instanceof Reference) {
1491                  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));
1492              } elseif ($value instanceof Expression) {
1493                  throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key));
1494              } else {
1495                  $value = $this->export($value);
1496              }
1497  
1498              $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value);
1499          }
1500  
1501          return sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', $indent - 4));
1502      }
1503  
1504      /**
1505       * Ends the class definition.
1506       *
1507       * @return string
1508       */
1509      private function endClass()
1510      {
1511          return <<<'EOF'
1512  }
1513  
1514  EOF;
1515      }
1516  
1517      /**
1518       * Wraps the service conditionals.
1519       *
1520       * @param string $value
1521       * @param string $code
1522       *
1523       * @return string
1524       */
1525      private function wrapServiceConditionals($value, $code)
1526      {
1527          if (!$condition = $this->getServiceConditionals($value)) {
1528              return $code;
1529          }
1530  
1531          // re-indent the wrapped code
1532          $code = implode("\n", array_map(function ($line) { return $line ? '    '.$line : $line; }, explode("\n", $code)));
1533  
1534          return sprintf("        if (%s) {\n%s        }\n", $condition, $code);
1535      }
1536  
1537      /**
1538       * Get the conditions to execute for conditional services.
1539       *
1540       * @param string $value
1541       *
1542       * @return string|null
1543       */
1544      private function getServiceConditionals($value)
1545      {
1546          $conditions = [];
1547          foreach (ContainerBuilder::getInitializedConditionals($value) as $service) {
1548              if (!$this->container->hasDefinition($service)) {
1549                  return 'false';
1550              }
1551              $conditions[] = sprintf('isset($this->services[%s])', $this->doExport($service));
1552          }
1553          foreach (ContainerBuilder::getServiceConditionals($value) as $service) {
1554              if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) {
1555                  continue;
1556              }
1557  
1558              $conditions[] = sprintf('$this->has(%s)', $this->doExport($service));
1559          }
1560  
1561          if (!$conditions) {
1562              return '';
1563          }
1564  
1565          return implode(' && ', $conditions);
1566      }
1567  
1568      private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [], $byConstructor = null)
1569      {
1570          if (null === $definitions) {
1571              $definitions = new \SplObjectStorage();
1572          }
1573  
1574          foreach ($arguments as $argument) {
1575              if (\is_array($argument)) {
1576                  $this->getDefinitionsFromArguments($argument, $definitions, $calls, $byConstructor);
1577              } elseif ($argument instanceof Reference) {
1578                  $id = $this->container->normalizeId($argument);
1579  
1580                  while ($this->container->hasAlias($id)) {
1581                      $id = (string) $this->container->getAlias($id);
1582                  }
1583  
1584                  if (!isset($calls[$id])) {
1585                      $calls[$id] = [0, $argument->getInvalidBehavior(), $byConstructor];
1586                  } else {
1587                      $calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
1588                  }
1589  
1590                  ++$calls[$id][0];
1591              } elseif (!$argument instanceof Definition) {
1592                  // no-op
1593              } elseif (isset($definitions[$argument])) {
1594                  $definitions[$argument] = 1 + $definitions[$argument];
1595              } else {
1596                  $definitions[$argument] = 1;
1597                  $arguments = [$argument->getArguments(), $argument->getFactory()];
1598                  $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null === $byConstructor || $byConstructor);
1599                  $arguments = [$argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()];
1600                  $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null !== $byConstructor && $byConstructor);
1601              }
1602          }
1603  
1604          return $definitions;
1605      }
1606  
1607      /**
1608       * Dumps values.
1609       *
1610       * @param mixed $value
1611       * @param bool  $interpolate
1612       *
1613       * @return string
1614       *
1615       * @throws RuntimeException
1616       */
1617      private function dumpValue($value, $interpolate = true)
1618      {
1619          if (\is_array($value)) {
1620              if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) {
1621                  return $this->dumpValue("%$param%");
1622              }
1623              $code = [];
1624              foreach ($value as $k => $v) {
1625                  $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1626              }
1627  
1628              return sprintf('[%s]', implode(', ', $code));
1629          } elseif ($value instanceof ArgumentInterface) {
1630              $scope = [$this->definitionVariables, $this->referenceVariables];
1631              $this->definitionVariables = $this->referenceVariables = null;
1632  
1633              try {
1634                  if ($value instanceof ServiceClosureArgument) {
1635                      $value = $value->getValues()[0];
1636                      $code = $this->dumpValue($value, $interpolate);
1637  
1638                      if ($value instanceof TypedReference) {
1639                          $code = sprintf('$f = function (\\%s $v%s) { return $v; }; return $f(%s);', $value->getType(), ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $value->getInvalidBehavior() ? ' = null' : '', $code);
1640                      } else {
1641                          $code = sprintf('return %s;', $code);
1642                      }
1643  
1644                      return sprintf("function () {\n            %s\n        }", $code);
1645                  }
1646  
1647                  if ($value instanceof IteratorArgument) {
1648                      $operands = [0];
1649                      $code = [];
1650                      $code[] = 'new RewindableGenerator(function () {';
1651  
1652                      if (!$values = $value->getValues()) {
1653                          $code[] = '            return new \EmptyIterator();';
1654                      } else {
1655                          $countCode = [];
1656                          $countCode[] = 'function () {';
1657  
1658                          foreach ($values as $k => $v) {
1659                              ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0];
1660                              $v = $this->wrapServiceConditionals($v, sprintf("        yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)));
1661                              foreach (explode("\n", $v) as $v) {
1662                                  if ($v) {
1663                                      $code[] = '    '.$v;
1664                                  }
1665                              }
1666                          }
1667  
1668                          $countCode[] = sprintf('            return %s;', implode(' + ', $operands));
1669                          $countCode[] = '        }';
1670                      }
1671  
1672                      $code[] = sprintf('        }, %s)', \count($operands) > 1 ? implode("\n", $countCode) : $operands[0]);
1673  
1674                      return implode("\n", $code);
1675                  }
1676              } finally {
1677                  list($this->definitionVariables, $this->referenceVariables) = $scope;
1678              }
1679          } elseif ($value instanceof Definition) {
1680              if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
1681                  return $this->dumpValue($this->definitionVariables[$value], $interpolate);
1682              }
1683              if ($value->getMethodCalls()) {
1684                  throw new RuntimeException('Cannot dump definitions which have method calls.');
1685              }
1686              if ($value->getProperties()) {
1687                  throw new RuntimeException('Cannot dump definitions which have properties.');
1688              }
1689              if (null !== $value->getConfigurator()) {
1690                  throw new RuntimeException('Cannot dump definitions which have a configurator.');
1691              }
1692  
1693              $arguments = [];
1694              foreach ($value->getArguments() as $argument) {
1695                  $arguments[] = $this->dumpValue($argument);
1696              }
1697  
1698              if (null !== $value->getFactory()) {
1699                  $factory = $value->getFactory();
1700  
1701                  if (\is_string($factory)) {
1702                      return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments));
1703                  }
1704  
1705                  if (\is_array($factory)) {
1706                      if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) {
1707                          throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $factory[1] ?: 'n/a'));
1708                      }
1709  
1710                      $class = $this->dumpValue($factory[0]);
1711                      if (\is_string($factory[0])) {
1712                          return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments));
1713                      }
1714  
1715                      if ($factory[0] instanceof Definition) {
1716                          if (0 === strpos($class, 'new ')) {
1717                              return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments));
1718                          }
1719  
1720                          return sprintf("\\call_user_func([%s, '%s']%s)", $class, $factory[1], \count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
1721                      }
1722  
1723                      if ($factory[0] instanceof Reference) {
1724                          return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments));
1725                      }
1726                  }
1727  
1728                  throw new RuntimeException('Cannot dump definition because of invalid factory.');
1729              }
1730  
1731              $class = $value->getClass();
1732              if (null === $class) {
1733                  throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
1734              }
1735  
1736              return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments));
1737          } elseif ($value instanceof Variable) {
1738              return '$'.$value;
1739          } elseif ($value instanceof Reference) {
1740              $id = $this->container->normalizeId($value);
1741  
1742              while ($this->container->hasAlias($id)) {
1743                  $id = (string) $this->container->getAlias($id);
1744              }
1745  
1746              if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) {
1747                  return $this->dumpValue($this->referenceVariables[$id], $interpolate);
1748              }
1749  
1750              return $this->getServiceCall($id, $value);
1751          } elseif ($value instanceof Expression) {
1752              return $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']);
1753          } elseif ($value instanceof Parameter) {
1754              return $this->dumpParameter($value);
1755          } elseif (true === $interpolate && \is_string($value)) {
1756              if (preg_match('/^%([^%]+)%$/', $value, $match)) {
1757                  // we do this to deal with non string values (Boolean, integer, ...)
1758                  // the preg_replace_callback converts them to strings
1759                  return $this->dumpParameter($match[1]);
1760              } else {
1761                  $replaceParameters = function ($match) {
1762                      return "'.".$this->dumpParameter($match[2]).".'";
1763                  };
1764  
1765                  $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
1766  
1767                  return $code;
1768              }
1769          } elseif (\is_object($value) || \is_resource($value)) {
1770              throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
1771          }
1772  
1773          return $this->export($value);
1774      }
1775  
1776      /**
1777       * Dumps a string to a literal (aka PHP Code) class value.
1778       *
1779       * @param string $class
1780       *
1781       * @return string
1782       *
1783       * @throws RuntimeException
1784       */
1785      private function dumpLiteralClass($class)
1786      {
1787          if (false !== strpos($class, '$')) {
1788              return sprintf('${($_ = %s) && false ?: "_"}', $class);
1789          }
1790          if (0 !== strpos($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
1791              throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s).', $class ?: 'n/a'));
1792          }
1793  
1794          $class = substr(str_replace('\\\\', '\\', $class), 1, -1);
1795  
1796          return 0 === strpos($class, '\\') ? $class : '\\'.$class;
1797      }
1798  
1799      /**
1800       * Dumps a parameter.
1801       *
1802       * @param string $name
1803       *
1804       * @return string
1805       */
1806      private function dumpParameter($name)
1807      {
1808          $name = (string) $name;
1809  
1810          if ($this->container->isCompiled() && $this->container->hasParameter($name)) {
1811              $value = $this->container->getParameter($name);
1812              $dumpedValue = $this->dumpValue($value, false);
1813  
1814              if (!$value || !\is_array($value)) {
1815                  return $dumpedValue;
1816              }
1817  
1818              if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) {
1819                  return sprintf('$this->parameters[%s]', $this->doExport($name));
1820              }
1821          }
1822  
1823          return sprintf('$this->getParameter(%s)', $this->doExport($name));
1824      }
1825  
1826      /**
1827       * Gets a service call.
1828       *
1829       * @param string    $id
1830       * @param Reference $reference
1831       *
1832       * @return string
1833       */
1834      private function getServiceCall($id, Reference $reference = null)
1835      {
1836          while ($this->container->hasAlias($id)) {
1837              $id = (string) $this->container->getAlias($id);
1838          }
1839          $id = $this->container->normalizeId($id);
1840  
1841          if ('service_container' === $id) {
1842              return '$this';
1843          }
1844  
1845          if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) {
1846              if ($definition->isSynthetic()) {
1847                  $code = sprintf('$this->get(%s%s)', $this->doExport($id), null !== $reference ? ', '.$reference->getInvalidBehavior() : '');
1848              } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
1849                  $code = 'null';
1850                  if (!$definition->isShared()) {
1851                      return $code;
1852                  }
1853              } elseif ($this->isTrivialInstance($definition)) {
1854                  $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2);
1855                  if ($definition->isShared()) {
1856                      $code = sprintf('$this->services[%s] = %s', $this->doExport($id), $code);
1857                  }
1858                  $code = "($code)";
1859              } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) {
1860                  $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id));
1861              } else {
1862                  $code = sprintf('$this->%s()', $this->generateMethodName($id));
1863              }
1864          } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
1865              return 'null';
1866          } elseif (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
1867              $code = sprintf('$this->get(%s, /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $this->doExport($id), ContainerInterface::NULL_ON_INVALID_REFERENCE);
1868          } else {
1869              $code = sprintf('$this->get(%s)', $this->doExport($id));
1870          }
1871  
1872          // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? $code)" on PHP>=7.0
1873  
1874          return sprintf("\${(\$_ = isset(\$this->services[%s]) ? \$this->services[%1\$s] : %s) && false ?: '_'}", $this->doExport($id), $code);
1875      }
1876  
1877      /**
1878       * Initializes the method names map to avoid conflicts with the Container methods.
1879       *
1880       * @param string $class the container base class
1881       */
1882      private function initializeMethodNamesMap($class)
1883      {
1884          $this->serviceIdToMethodNameMap = [];
1885          $this->usedMethodNames = [];
1886  
1887          if ($reflectionClass = $this->container->getReflectionClass($class)) {
1888              foreach ($reflectionClass->getMethods() as $method) {
1889                  $this->usedMethodNames[strtolower($method->getName())] = true;
1890              }
1891          }
1892      }
1893  
1894      /**
1895       * Convert a service id to a valid PHP method name.
1896       *
1897       * @param string $id
1898       *
1899       * @return string
1900       *
1901       * @throws InvalidArgumentException
1902       */
1903      private function generateMethodName($id)
1904      {
1905          if (isset($this->serviceIdToMethodNameMap[$id])) {
1906              return $this->serviceIdToMethodNameMap[$id];
1907          }
1908  
1909          $i = strrpos($id, '\\');
1910          $name = Container::camelize(false !== $i && isset($id[1 + $i]) ? substr($id, 1 + $i) : $id);
1911          $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name);
1912          $methodName = 'get'.$name.'Service';
1913          $suffix = 1;
1914  
1915          while (isset($this->usedMethodNames[strtolower($methodName)])) {
1916              ++$suffix;
1917              $methodName = 'get'.$name.$suffix.'Service';
1918          }
1919  
1920          $this->serviceIdToMethodNameMap[$id] = $methodName;
1921          $this->usedMethodNames[strtolower($methodName)] = true;
1922  
1923          return $methodName;
1924      }
1925  
1926      /**
1927       * Returns the next name to use.
1928       *
1929       * @return string
1930       */
1931      private function getNextVariableName()
1932      {
1933          $firstChars = self::FIRST_CHARS;
1934          $firstCharsLength = \strlen($firstChars);
1935          $nonFirstChars = self::NON_FIRST_CHARS;
1936          $nonFirstCharsLength = \strlen($nonFirstChars);
1937  
1938          while (true) {
1939              $name = '';
1940              $i = $this->variableCount;
1941  
1942              if ('' === $name) {
1943                  $name .= $firstChars[$i % $firstCharsLength];
1944                  $i = (int) ($i / $firstCharsLength);
1945              }
1946  
1947              while ($i > 0) {
1948                  --$i;
1949                  $name .= $nonFirstChars[$i % $nonFirstCharsLength];
1950                  $i = (int) ($i / $nonFirstCharsLength);
1951              }
1952  
1953              ++$this->variableCount;
1954  
1955              // check that the name is not reserved
1956              if (\in_array($name, $this->reservedVariables, true)) {
1957                  continue;
1958              }
1959  
1960              return $name;
1961          }
1962      }
1963  
1964      private function getExpressionLanguage()
1965      {
1966          if (null === $this->expressionLanguage) {
1967              if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
1968                  throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
1969              }
1970              $providers = $this->container->getExpressionLanguageProviders();
1971              $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
1972                  $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null;
1973  
1974                  if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) {
1975                      return $this->getServiceCall($id);
1976                  }
1977  
1978                  return sprintf('$this->get(%s)', $arg);
1979              });
1980  
1981              if ($this->container->isTrackingResources()) {
1982                  foreach ($providers as $provider) {
1983                      $this->container->addObjectResource($provider);
1984                  }
1985              }
1986          }
1987  
1988          return $this->expressionLanguage;
1989      }
1990  
1991      private function isHotPath(Definition $definition)
1992      {
1993          return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated();
1994      }
1995  
1996      private function export($value)
1997      {
1998          if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, \PREG_OFFSET_CAPTURE)) {
1999              $suffix = $matches[0][1] + \strlen($matches[0][0]);
2000              $matches[0][1] += \strlen($matches[1][0]);
2001              $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : '';
2002              $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : '';
2003              $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__';
2004              $offset = 2 + $this->targetDirMaxMatches - \count($matches);
2005  
2006              if ($this->asFiles || 0 < $offset) {
2007                  $dirname = sprintf('$this->targetDirs[%d]', $offset);
2008              }
2009  
2010              if ($prefix || $suffix) {
2011                  return sprintf('(%s%s%s)', $prefix, $dirname, $suffix);
2012              }
2013  
2014              return $dirname;
2015          }
2016  
2017          return $this->doExport($value, true);
2018      }
2019  
2020      private function doExport($value, $resolveEnv = false)
2021      {
2022          if (\is_string($value) && false !== strpos($value, "\n")) {
2023              $cleanParts = explode("\n", $value);
2024              $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts);
2025              $export = implode('."\n".', $cleanParts);
2026          } else {
2027              $export = var_export($value, true);
2028          }
2029  
2030          if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) {
2031              $export = $resolvedExport;
2032              if (".''" === substr($export, -3)) {
2033                  $export = substr($export, 0, -3);
2034                  if ("'" === $export[1]) {
2035                      $export = substr_replace($export, '', 18, 7);
2036                  }
2037              }
2038              if ("'" === $export[1]) {
2039                  $export = substr($export, 3);
2040              }
2041          }
2042  
2043          return $export;
2044      }
2045  }


Generated: Thu Mar 24 21:31:15 2022 Cross-referenced by PHPXref 0.7.1