PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

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