[ Index ]

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


Generated: Tue Apr 7 19:44:41 2020 Cross-referenced by PHPXref 0.7.1