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