PageRenderTime 239ms CodeModel.GetById 100ms app.highlight 71ms RepoModel.GetById 60ms app.codeStats 0ms

/wwwroot/phpbb/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ContainerBuilder.php

https://github.com/spring/spring-website
PHP | 1158 lines | 503 code | 149 blank | 506 comment | 74 complexity | 155e4564c970d5d2c412a0ea74eaf369 MD5 | raw file
   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
  12namespace Symfony\Component\DependencyInjection;
  13
  14use Symfony\Component\DependencyInjection\Compiler\Compiler;
  15use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  16use Symfony\Component\DependencyInjection\Compiler\PassConfig;
  17use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
  18use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
  19use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  20use Symfony\Component\DependencyInjection\Exception\LogicException;
  21use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  22use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
  23use Symfony\Component\Config\Resource\FileResource;
  24use Symfony\Component\Config\Resource\ResourceInterface;
  25use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
  26use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
  27
  28/**
  29 * ContainerBuilder is a DI container that provides an API to easily describe services.
  30 *
  31 * @author Fabien Potencier <fabien@symfony.com>
  32 *
  33 * @api
  34 */
  35class ContainerBuilder extends Container implements TaggedContainerInterface
  36{
  37    /**
  38     * @var ExtensionInterface[]
  39     */
  40    private $extensions = array();
  41
  42    /**
  43     * @var ExtensionInterface[]
  44     */
  45    private $extensionsByNs = array();
  46
  47    /**
  48     * @var Definition[]
  49     */
  50    private $definitions = array();
  51
  52    /**
  53     * @var Definition[]
  54     */
  55    private $obsoleteDefinitions = array();
  56
  57    /**
  58     * @var Alias[]
  59     */
  60    private $aliasDefinitions = array();
  61
  62    /**
  63     * @var ResourceInterface[]
  64     */
  65    private $resources = array();
  66
  67    private $extensionConfigs = array();
  68
  69    /**
  70     * @var Compiler
  71     */
  72    private $compiler;
  73
  74    private $trackResources = true;
  75
  76    /**
  77     * @var InstantiatorInterface|null
  78     */
  79    private $proxyInstantiator;
  80
  81    /**
  82     * Sets the track resources flag.
  83     *
  84     * If you are not using the loaders and therefore don't want
  85     * to depend on the Config component, set this flag to false.
  86     *
  87     * @param bool    $track true if you want to track resources, false otherwise
  88     */
  89    public function setResourceTracking($track)
  90    {
  91        $this->trackResources = (bool) $track;
  92    }
  93
  94    /**
  95     * Checks if resources are tracked.
  96     *
  97     * @return bool    true if resources are tracked, false otherwise
  98     */
  99    public function isTrackingResources()
 100    {
 101        return $this->trackResources;
 102    }
 103
 104    /**
 105     * Sets the instantiator to be used when fetching proxies.
 106     *
 107     * @param InstantiatorInterface $proxyInstantiator
 108     */
 109    public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
 110    {
 111        $this->proxyInstantiator = $proxyInstantiator;
 112    }
 113
 114    /**
 115     * Registers an extension.
 116     *
 117     * @param ExtensionInterface $extension An extension instance
 118     *
 119     * @api
 120     */
 121    public function registerExtension(ExtensionInterface $extension)
 122    {
 123        $this->extensions[$extension->getAlias()] = $extension;
 124
 125        if (false !== $extension->getNamespace()) {
 126            $this->extensionsByNs[$extension->getNamespace()] = $extension;
 127        }
 128    }
 129
 130    /**
 131     * Returns an extension by alias or namespace.
 132     *
 133     * @param string $name An alias or a namespace
 134     *
 135     * @return ExtensionInterface An extension instance
 136     *
 137     * @throws LogicException if the extension is not registered
 138     *
 139     * @api
 140     */
 141    public function getExtension($name)
 142    {
 143        if (isset($this->extensions[$name])) {
 144            return $this->extensions[$name];
 145        }
 146
 147        if (isset($this->extensionsByNs[$name])) {
 148            return $this->extensionsByNs[$name];
 149        }
 150
 151        throw new LogicException(sprintf('Container extension "%s" is not registered', $name));
 152    }
 153
 154    /**
 155     * Returns all registered extensions.
 156     *
 157     * @return ExtensionInterface[] An array of ExtensionInterface
 158     *
 159     * @api
 160     */
 161    public function getExtensions()
 162    {
 163        return $this->extensions;
 164    }
 165
 166    /**
 167     * Checks if we have an extension.
 168     *
 169     * @param string $name The name of the extension
 170     *
 171     * @return bool    If the extension exists
 172     *
 173     * @api
 174     */
 175    public function hasExtension($name)
 176    {
 177        return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
 178    }
 179
 180    /**
 181     * Returns an array of resources loaded to build this configuration.
 182     *
 183     * @return ResourceInterface[] An array of resources
 184     *
 185     * @api
 186     */
 187    public function getResources()
 188    {
 189        return array_unique($this->resources);
 190    }
 191
 192    /**
 193     * Adds a resource for this configuration.
 194     *
 195     * @param ResourceInterface $resource A resource instance
 196     *
 197     * @return ContainerBuilder The current instance
 198     *
 199     * @api
 200     */
 201    public function addResource(ResourceInterface $resource)
 202    {
 203        if (!$this->trackResources) {
 204            return $this;
 205        }
 206
 207        $this->resources[] = $resource;
 208
 209        return $this;
 210    }
 211
 212    /**
 213     * Sets the resources for this configuration.
 214     *
 215     * @param ResourceInterface[] $resources An array of resources
 216     *
 217     * @return ContainerBuilder The current instance
 218     *
 219     * @api
 220     */
 221    public function setResources(array $resources)
 222    {
 223        if (!$this->trackResources) {
 224            return $this;
 225        }
 226
 227        $this->resources = $resources;
 228
 229        return $this;
 230    }
 231
 232    /**
 233     * Adds the object class hierarchy as resources.
 234     *
 235     * @param object $object An object instance
 236     *
 237     * @return ContainerBuilder The current instance
 238     *
 239     * @api
 240     */
 241    public function addObjectResource($object)
 242    {
 243        if ($this->trackResources) {
 244            $this->addClassResource(new \ReflectionClass($object));
 245        }
 246
 247        return $this;
 248    }
 249
 250    /**
 251     * Adds the given class hierarchy as resources.
 252     *
 253     * @param \ReflectionClass $class
 254     *
 255     * @return ContainerBuilder The current instance
 256     */
 257    public function addClassResource(\ReflectionClass $class)
 258    {
 259        if (!$this->trackResources) {
 260            return $this;
 261        }
 262
 263        do {
 264            $this->addResource(new FileResource($class->getFileName()));
 265        } while ($class = $class->getParentClass());
 266
 267        return $this;
 268    }
 269
 270    /**
 271     * Loads the configuration for an extension.
 272     *
 273     * @param string $extension The extension alias or namespace
 274     * @param array  $values    An array of values that customizes the extension
 275     *
 276     * @return ContainerBuilder The current instance
 277     * @throws BadMethodCallException When this ContainerBuilder is frozen
 278     *
 279     * @throws \LogicException if the container is frozen
 280     *
 281     * @api
 282     */
 283    public function loadFromExtension($extension, array $values = array())
 284    {
 285        if ($this->isFrozen()) {
 286            throw new BadMethodCallException('Cannot load from an extension on a frozen container.');
 287        }
 288
 289        $namespace = $this->getExtension($extension)->getAlias();
 290
 291        $this->extensionConfigs[$namespace][] = $values;
 292
 293        return $this;
 294    }
 295
 296    /**
 297     * Adds a compiler pass.
 298     *
 299     * @param CompilerPassInterface $pass A compiler pass
 300     * @param string                $type The type of compiler pass
 301     *
 302     * @return ContainerBuilder The current instance
 303     *
 304     * @api
 305     */
 306    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
 307    {
 308        if (null === $this->compiler) {
 309            $this->compiler = new Compiler();
 310        }
 311
 312        $this->compiler->addPass($pass, $type);
 313
 314        $this->addObjectResource($pass);
 315
 316        return $this;
 317    }
 318
 319    /**
 320     * Returns the compiler pass config which can then be modified.
 321     *
 322     * @return PassConfig The compiler pass config
 323     *
 324     * @api
 325     */
 326    public function getCompilerPassConfig()
 327    {
 328        if (null === $this->compiler) {
 329            $this->compiler = new Compiler();
 330        }
 331
 332        return $this->compiler->getPassConfig();
 333    }
 334
 335    /**
 336     * Returns the compiler.
 337     *
 338     * @return Compiler The compiler
 339     *
 340     * @api
 341     */
 342    public function getCompiler()
 343    {
 344        if (null === $this->compiler) {
 345            $this->compiler = new Compiler();
 346        }
 347
 348        return $this->compiler;
 349    }
 350
 351    /**
 352     * Returns all Scopes.
 353     *
 354     * @return array An array of scopes
 355     *
 356     * @api
 357     */
 358    public function getScopes()
 359    {
 360        return $this->scopes;
 361    }
 362
 363    /**
 364     * Returns all Scope children.
 365     *
 366     * @return array An array of scope children.
 367     *
 368     * @api
 369     */
 370    public function getScopeChildren()
 371    {
 372        return $this->scopeChildren;
 373    }
 374
 375    /**
 376     * Sets a service.
 377     *
 378     * @param string $id      The service identifier
 379     * @param object $service The service instance
 380     * @param string $scope   The scope
 381     *
 382     * @throws BadMethodCallException When this ContainerBuilder is frozen
 383     *
 384     * @api
 385     */
 386    public function set($id, $service, $scope = self::SCOPE_CONTAINER)
 387    {
 388        $id = strtolower($id);
 389
 390        if ($this->isFrozen()) {
 391            // setting a synthetic service on a frozen container is alright
 392            if (
 393                (!isset($this->definitions[$id]) && !isset($this->obsoleteDefinitions[$id]))
 394                    ||
 395                (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())
 396                    ||
 397                (isset($this->obsoleteDefinitions[$id]) && !$this->obsoleteDefinitions[$id]->isSynthetic())
 398            ) {
 399                throw new BadMethodCallException(sprintf('Setting service "%s" on a frozen container is not allowed.', $id));
 400            }
 401        }
 402
 403        if (isset($this->definitions[$id])) {
 404            $this->obsoleteDefinitions[$id] = $this->definitions[$id];
 405        }
 406
 407        unset($this->definitions[$id], $this->aliasDefinitions[$id]);
 408
 409        parent::set($id, $service, $scope);
 410
 411        if (isset($this->obsoleteDefinitions[$id]) && $this->obsoleteDefinitions[$id]->isSynchronized()) {
 412            $this->synchronize($id);
 413        }
 414    }
 415
 416    /**
 417     * Removes a service definition.
 418     *
 419     * @param string $id The service identifier
 420     *
 421     * @api
 422     */
 423    public function removeDefinition($id)
 424    {
 425        unset($this->definitions[strtolower($id)]);
 426    }
 427
 428    /**
 429     * Returns true if the given service is defined.
 430     *
 431     * @param string $id The service identifier
 432     *
 433     * @return bool    true if the service is defined, false otherwise
 434     *
 435     * @api
 436     */
 437    public function has($id)
 438    {
 439        $id = strtolower($id);
 440
 441        return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
 442    }
 443
 444    /**
 445     * Gets a service.
 446     *
 447     * @param string  $id              The service identifier
 448     * @param int     $invalidBehavior The behavior when the service does not exist
 449     *
 450     * @return object The associated service
 451     *
 452     * @throws InvalidArgumentException when no definitions are available
 453     * @throws InactiveScopeException   when the current scope is not active
 454     * @throws LogicException           when a circular dependency is detected
 455     * @throws \Exception
 456     *
 457     * @see Reference
 458     *
 459     * @api
 460     */
 461    public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
 462    {
 463        $id = strtolower($id);
 464
 465        if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
 466            return $service;
 467        }
 468
 469        if (isset($this->loading[$id])) {
 470            throw new LogicException(sprintf('The service "%s" has a circular reference to itself.', $id));
 471        }
 472
 473        if (!$this->hasDefinition($id) && isset($this->aliasDefinitions[$id])) {
 474            return $this->get($this->aliasDefinitions[$id]);
 475        }
 476
 477        try {
 478            $definition = $this->getDefinition($id);
 479        } catch (InvalidArgumentException $e) {
 480            if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
 481                return;
 482            }
 483
 484            throw $e;
 485        }
 486
 487        $this->loading[$id] = true;
 488
 489        try {
 490            $service = $this->createService($definition, $id);
 491        } catch (\Exception $e) {
 492            unset($this->loading[$id]);
 493
 494            if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
 495                return;
 496            }
 497
 498            throw $e;
 499        }
 500
 501        unset($this->loading[$id]);
 502
 503        return $service;
 504    }
 505
 506    /**
 507     * Merges a ContainerBuilder with the current ContainerBuilder configuration.
 508     *
 509     * Service definitions overrides the current defined ones.
 510     *
 511     * But for parameters, they are overridden by the current ones. It allows
 512     * the parameters passed to the container constructor to have precedence
 513     * over the loaded ones.
 514     *
 515     * $container = new ContainerBuilder(array('foo' => 'bar'));
 516     * $loader = new LoaderXXX($container);
 517     * $loader->load('resource_name');
 518     * $container->register('foo', new stdClass());
 519     *
 520     * In the above example, even if the loaded resource defines a foo
 521     * parameter, the value will still be 'bar' as defined in the ContainerBuilder
 522     * constructor.
 523     *
 524     * @param ContainerBuilder $container The ContainerBuilder instance to merge.
 525     *
 526     *
 527     * @throws BadMethodCallException When this ContainerBuilder is frozen
 528     *
 529     * @api
 530     */
 531    public function merge(ContainerBuilder $container)
 532    {
 533        if ($this->isFrozen()) {
 534            throw new BadMethodCallException('Cannot merge on a frozen container.');
 535        }
 536
 537        $this->addDefinitions($container->getDefinitions());
 538        $this->addAliases($container->getAliases());
 539        $this->getParameterBag()->add($container->getParameterBag()->all());
 540
 541        if ($this->trackResources) {
 542            foreach ($container->getResources() as $resource) {
 543                $this->addResource($resource);
 544            }
 545        }
 546
 547        foreach ($this->extensions as $name => $extension) {
 548            if (!isset($this->extensionConfigs[$name])) {
 549                $this->extensionConfigs[$name] = array();
 550            }
 551
 552            $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
 553        }
 554    }
 555
 556    /**
 557     * Returns the configuration array for the given extension.
 558     *
 559     * @param string $name The name of the extension
 560     *
 561     * @return array An array of configuration
 562     *
 563     * @api
 564     */
 565    public function getExtensionConfig($name)
 566    {
 567        if (!isset($this->extensionConfigs[$name])) {
 568            $this->extensionConfigs[$name] = array();
 569        }
 570
 571        return $this->extensionConfigs[$name];
 572    }
 573
 574    /**
 575     * Prepends a config array to the configs of the given extension.
 576     *
 577     * @param string $name    The name of the extension
 578     * @param array  $config  The config to set
 579     */
 580    public function prependExtensionConfig($name, array $config)
 581    {
 582        if (!isset($this->extensionConfigs[$name])) {
 583            $this->extensionConfigs[$name] = array();
 584        }
 585
 586        array_unshift($this->extensionConfigs[$name], $config);
 587    }
 588
 589    /**
 590     * Compiles the container.
 591     *
 592     * This method passes the container to compiler
 593     * passes whose job is to manipulate and optimize
 594     * the container.
 595     *
 596     * The main compiler passes roughly do four things:
 597     *
 598     *  * The extension configurations are merged;
 599     *  * Parameter values are resolved;
 600     *  * The parameter bag is frozen;
 601     *  * Extension loading is disabled.
 602     *
 603     * @api
 604     */
 605    public function compile()
 606    {
 607        if (null === $this->compiler) {
 608            $this->compiler = new Compiler();
 609        }
 610
 611        if ($this->trackResources) {
 612            foreach ($this->compiler->getPassConfig()->getPasses() as $pass) {
 613                $this->addObjectResource($pass);
 614            }
 615        }
 616
 617        $this->compiler->compile($this);
 618
 619        if ($this->trackResources) {
 620            foreach ($this->definitions as $definition) {
 621                if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) {
 622                    $this->addClassResource(new \ReflectionClass($class));
 623                }
 624            }
 625        }
 626
 627        $this->extensionConfigs = array();
 628
 629        parent::compile();
 630    }
 631
 632    /**
 633     * Gets all service ids.
 634     *
 635     * @return array An array of all defined service ids
 636     */
 637    public function getServiceIds()
 638    {
 639        return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()));
 640    }
 641
 642    /**
 643     * Adds the service aliases.
 644     *
 645     * @param array $aliases An array of aliases
 646     *
 647     * @api
 648     */
 649    public function addAliases(array $aliases)
 650    {
 651        foreach ($aliases as $alias => $id) {
 652            $this->setAlias($alias, $id);
 653        }
 654    }
 655
 656    /**
 657     * Sets the service aliases.
 658     *
 659     * @param array $aliases An array of aliases
 660     *
 661     * @api
 662     */
 663    public function setAliases(array $aliases)
 664    {
 665        $this->aliasDefinitions = array();
 666        $this->addAliases($aliases);
 667    }
 668
 669    /**
 670     * Sets an alias for an existing service.
 671     *
 672     * @param string        $alias The alias to create
 673     * @param string|Alias  $id    The service to alias
 674     *
 675     * @throws InvalidArgumentException if the id is not a string or an Alias
 676     * @throws InvalidArgumentException if the alias is for itself
 677     *
 678     * @api
 679     */
 680    public function setAlias($alias, $id)
 681    {
 682        $alias = strtolower($alias);
 683
 684        if (is_string($id)) {
 685            $id = new Alias($id);
 686        } elseif (!$id instanceof Alias) {
 687            throw new InvalidArgumentException('$id must be a string, or an Alias object.');
 688        }
 689
 690        if ($alias === strtolower($id)) {
 691            throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
 692        }
 693
 694        unset($this->definitions[$alias]);
 695
 696        $this->aliasDefinitions[$alias] = $id;
 697    }
 698
 699    /**
 700     * Removes an alias.
 701     *
 702     * @param string $alias The alias to remove
 703     *
 704     * @api
 705     */
 706    public function removeAlias($alias)
 707    {
 708        unset($this->aliasDefinitions[strtolower($alias)]);
 709    }
 710
 711    /**
 712     * Returns true if an alias exists under the given identifier.
 713     *
 714     * @param string $id The service identifier
 715     *
 716     * @return bool    true if the alias exists, false otherwise
 717     *
 718     * @api
 719     */
 720    public function hasAlias($id)
 721    {
 722        return isset($this->aliasDefinitions[strtolower($id)]);
 723    }
 724
 725    /**
 726     * Gets all defined aliases.
 727     *
 728     * @return Alias[] An array of aliases
 729     *
 730     * @api
 731     */
 732    public function getAliases()
 733    {
 734        return $this->aliasDefinitions;
 735    }
 736
 737    /**
 738     * Gets an alias.
 739     *
 740     * @param string $id The service identifier
 741     *
 742     * @return Alias An Alias instance
 743     *
 744     * @throws InvalidArgumentException if the alias does not exist
 745     *
 746     * @api
 747     */
 748    public function getAlias($id)
 749    {
 750        $id = strtolower($id);
 751
 752        if (!$this->hasAlias($id)) {
 753            throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
 754        }
 755
 756        return $this->aliasDefinitions[$id];
 757    }
 758
 759    /**
 760     * Registers a service definition.
 761     *
 762     * This methods allows for simple registration of service definition
 763     * with a fluid interface.
 764     *
 765     * @param string $id    The service identifier
 766     * @param string $class The service class
 767     *
 768     * @return Definition A Definition instance
 769     *
 770     * @api
 771     */
 772    public function register($id, $class = null)
 773    {
 774        return $this->setDefinition(strtolower($id), new Definition($class));
 775    }
 776
 777    /**
 778     * Adds the service definitions.
 779     *
 780     * @param Definition[] $definitions An array of service definitions
 781     *
 782     * @api
 783     */
 784    public function addDefinitions(array $definitions)
 785    {
 786        foreach ($definitions as $id => $definition) {
 787            $this->setDefinition($id, $definition);
 788        }
 789    }
 790
 791    /**
 792     * Sets the service definitions.
 793     *
 794     * @param Definition[] $definitions An array of service definitions
 795     *
 796     * @api
 797     */
 798    public function setDefinitions(array $definitions)
 799    {
 800        $this->definitions = array();
 801        $this->addDefinitions($definitions);
 802    }
 803
 804    /**
 805     * Gets all service definitions.
 806     *
 807     * @return Definition[] An array of Definition instances
 808     *
 809     * @api
 810     */
 811    public function getDefinitions()
 812    {
 813        return $this->definitions;
 814    }
 815
 816    /**
 817     * Sets a service definition.
 818     *
 819     * @param string     $id         The service identifier
 820     * @param Definition $definition A Definition instance
 821     *
 822     * @return Definition the service definition
 823     *
 824     * @throws BadMethodCallException When this ContainerBuilder is frozen
 825     *
 826     * @api
 827     */
 828    public function setDefinition($id, Definition $definition)
 829    {
 830        if ($this->isFrozen()) {
 831            throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
 832        }
 833
 834        $id = strtolower($id);
 835
 836        unset($this->aliasDefinitions[$id]);
 837
 838        return $this->definitions[$id] = $definition;
 839    }
 840
 841    /**
 842     * Returns true if a service definition exists under the given identifier.
 843     *
 844     * @param string $id The service identifier
 845     *
 846     * @return bool    true if the service definition exists, false otherwise
 847     *
 848     * @api
 849     */
 850    public function hasDefinition($id)
 851    {
 852        return array_key_exists(strtolower($id), $this->definitions);
 853    }
 854
 855    /**
 856     * Gets a service definition.
 857     *
 858     * @param string $id The service identifier
 859     *
 860     * @return Definition A Definition instance
 861     *
 862     * @throws InvalidArgumentException if the service definition does not exist
 863     *
 864     * @api
 865     */
 866    public function getDefinition($id)
 867    {
 868        $id = strtolower($id);
 869
 870        if (!$this->hasDefinition($id)) {
 871            throw new InvalidArgumentException(sprintf('The service definition "%s" does not exist.', $id));
 872        }
 873
 874        return $this->definitions[$id];
 875    }
 876
 877    /**
 878     * Gets a service definition by id or alias.
 879     *
 880     * The method "unaliases" recursively to return a Definition instance.
 881     *
 882     * @param string $id The service identifier or alias
 883     *
 884     * @return Definition A Definition instance
 885     *
 886     * @throws InvalidArgumentException if the service definition does not exist
 887     *
 888     * @api
 889     */
 890    public function findDefinition($id)
 891    {
 892        while ($this->hasAlias($id)) {
 893            $id = (string) $this->getAlias($id);
 894        }
 895
 896        return $this->getDefinition($id);
 897    }
 898
 899    /**
 900     * Creates a service for a service definition.
 901     *
 902     * @param Definition $definition A service definition instance
 903     * @param string     $id         The service identifier
 904     * @param bool       $tryProxy   Whether to try proxying the service with a lazy proxy
 905     *
 906     * @return object The service described by the service definition
 907     *
 908     * @throws RuntimeException When the scope is inactive
 909     * @throws RuntimeException When the factory definition is incomplete
 910     * @throws RuntimeException When the service is a synthetic service
 911     * @throws InvalidArgumentException When configure callable is not callable
 912     *
 913     * @internal this method is public because of PHP 5.3 limitations, do not use it explicitly in your code
 914     */
 915    public function createService(Definition $definition, $id, $tryProxy = true)
 916    {
 917        if ($definition->isSynthetic()) {
 918            throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
 919        }
 920
 921        if ($tryProxy && $definition->isLazy()) {
 922            $container = $this;
 923
 924            $proxy = $this
 925                ->getProxyInstantiator()
 926                ->instantiateProxy(
 927                    $container,
 928                    $definition,
 929                    $id, function () use ($definition, $id, $container) {
 930                        return $container->createService($definition, $id, false);
 931                    }
 932                );
 933            $this->shareService($definition, $proxy, $id);
 934
 935            return $proxy;
 936        }
 937
 938        $parameterBag = $this->getParameterBag();
 939
 940        if (null !== $definition->getFile()) {
 941            require_once $parameterBag->resolveValue($definition->getFile());
 942        }
 943
 944        $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
 945
 946        if (null !== $definition->getFactoryMethod()) {
 947            if (null !== $definition->getFactoryClass()) {
 948                $factory = $parameterBag->resolveValue($definition->getFactoryClass());
 949            } elseif (null !== $definition->getFactoryService()) {
 950                $factory = $this->get($parameterBag->resolveValue($definition->getFactoryService()));
 951            } else {
 952                throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $id));
 953            }
 954
 955            $service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments);
 956        } else {
 957            $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
 958
 959            $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
 960        }
 961
 962        if ($tryProxy || !$definition->isLazy()) {
 963            // share only if proxying failed, or if not a proxy
 964            $this->shareService($definition, $service, $id);
 965        }
 966
 967        foreach ($definition->getMethodCalls() as $call) {
 968            $this->callMethod($service, $call);
 969        }
 970
 971        $properties = $this->resolveServices($parameterBag->resolveValue($definition->getProperties()));
 972        foreach ($properties as $name => $value) {
 973            $service->$name = $value;
 974        }
 975
 976        if ($callable = $definition->getConfigurator()) {
 977            if (is_array($callable)) {
 978                $callable[0] = $callable[0] instanceof Reference ? $this->get((string) $callable[0]) : $parameterBag->resolveValue($callable[0]);
 979            }
 980
 981            if (!is_callable($callable)) {
 982                throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service)));
 983            }
 984
 985            call_user_func($callable, $service);
 986        }
 987
 988        return $service;
 989    }
 990
 991    /**
 992     * Replaces service references by the real service instance.
 993     *
 994     * @param mixed $value A value
 995     *
 996     * @return mixed The same value with all service references replaced by the real service instances
 997     */
 998    public function resolveServices($value)
 999    {
1000        if (is_array($value)) {
1001            foreach ($value as &$v) {
1002                $v = $this->resolveServices($v);
1003            }
1004        } elseif ($value instanceof Reference) {
1005            $value = $this->get((string) $value, $value->getInvalidBehavior());
1006        } elseif ($value instanceof Definition) {
1007            $value = $this->createService($value, null);
1008        }
1009
1010        return $value;
1011    }
1012
1013    /**
1014     * Returns service ids for a given tag.
1015     *
1016     * Example:
1017     *
1018     * $container->register('foo')->addTag('my.tag', array('hello' => 'world'));
1019     *
1020     * $serviceIds = $container->findTaggedServiceIds('my.tag');
1021     * foreach ($serviceIds as $serviceId => $tags) {
1022     *     foreach ($tags as $tag) {
1023     *         echo $tag['hello'];
1024     *     }
1025     * }
1026     *
1027     * @param string $name The tag name
1028     *
1029     * @return array An array of tags with the tagged service as key, holding a list of attribute arrays.
1030     *
1031     * @api
1032     */
1033    public function findTaggedServiceIds($name)
1034    {
1035        $tags = array();
1036        foreach ($this->getDefinitions() as $id => $definition) {
1037            if ($definition->hasTag($name)) {
1038                $tags[$id] = $definition->getTag($name);
1039            }
1040        }
1041
1042        return $tags;
1043    }
1044
1045    /**
1046     * Returns all tags the defined services use.
1047     *
1048     * @return array An array of tags
1049     */
1050    public function findTags()
1051    {
1052        $tags = array();
1053        foreach ($this->getDefinitions() as $id => $definition) {
1054            $tags = array_merge(array_keys($definition->getTags()), $tags);
1055        }
1056
1057        return array_unique($tags);
1058    }
1059
1060    /**
1061     * Returns the Service Conditionals.
1062     *
1063     * @param mixed $value An array of conditionals to return.
1064     *
1065     * @return array An array of Service conditionals
1066     */
1067    public static function getServiceConditionals($value)
1068    {
1069        $services = array();
1070
1071        if (is_array($value)) {
1072            foreach ($value as $v) {
1073                $services = array_unique(array_merge($services, self::getServiceConditionals($v)));
1074            }
1075        } elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
1076            $services[] = (string) $value;
1077        }
1078
1079        return $services;
1080    }
1081
1082    /**
1083     * Retrieves the currently set proxy instantiator or instantiates one.
1084     *
1085     * @return InstantiatorInterface
1086     */
1087    private function getProxyInstantiator()
1088    {
1089        if (!$this->proxyInstantiator) {
1090            $this->proxyInstantiator = new RealServiceInstantiator();
1091        }
1092
1093        return $this->proxyInstantiator;
1094    }
1095
1096    /**
1097     * Synchronizes a service change.
1098     *
1099     * This method updates all services that depend on the given
1100     * service by calling all methods referencing it.
1101     *
1102     * @param string $id A service id
1103     */
1104    private function synchronize($id)
1105    {
1106        foreach ($this->definitions as $definitionId => $definition) {
1107            // only check initialized services
1108            if (!$this->initialized($definitionId)) {
1109                continue;
1110            }
1111
1112            foreach ($definition->getMethodCalls() as $call) {
1113                foreach ($call[1] as $argument) {
1114                    if ($argument instanceof Reference && $id == (string) $argument) {
1115                        $this->callMethod($this->get($definitionId), $call);
1116                    }
1117                }
1118            }
1119        }
1120    }
1121
1122    private function callMethod($service, $call)
1123    {
1124        $services = self::getServiceConditionals($call[1]);
1125
1126        foreach ($services as $s) {
1127            if (!$this->has($s)) {
1128                return;
1129            }
1130        }
1131
1132        call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1])));
1133    }
1134
1135    /**
1136     * Shares a given service in the container
1137     *
1138     * @param Definition $definition
1139     * @param mixed      $service
1140     * @param string     $id
1141     *
1142     * @throws InactiveScopeException
1143     */
1144    private function shareService(Definition $definition, $service, $id)
1145    {
1146        if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
1147            if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) {
1148                throw new InactiveScopeException($id, $scope);
1149            }
1150
1151            $this->services[$lowerId = strtolower($id)] = $service;
1152
1153            if (self::SCOPE_CONTAINER !== $scope) {
1154                $this->scopedServices[$scope][$lowerId] = $service;
1155            }
1156        }
1157    }
1158}