PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/DependencyInjection/ContainerBuilder.php

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