PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

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

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