annotate vendor/symfony/dependency-injection/Dumper/PhpDumper.php @ 4:a9cd425dd02b

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