ContainerBuilder.php 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518
  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 Psr\Container\ContainerInterface as PsrContainerInterface;
  12. use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
  13. use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
  14. use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  15. use Symfony\Component\DependencyInjection\Compiler\Compiler;
  16. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  17. use Symfony\Component\DependencyInjection\Compiler\PassConfig;
  18. use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
  19. use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
  20. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  21. use Symfony\Component\DependencyInjection\Exception\LogicException;
  22. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  23. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  24. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  25. use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
  26. use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
  27. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  28. use Symfony\Component\Config\Resource\ClassExistenceResource;
  29. use Symfony\Component\Config\Resource\ComposerResource;
  30. use Symfony\Component\Config\Resource\DirectoryResource;
  31. use Symfony\Component\Config\Resource\FileExistenceResource;
  32. use Symfony\Component\Config\Resource\FileResource;
  33. use Symfony\Component\Config\Resource\GlobResource;
  34. use Symfony\Component\Config\Resource\ReflectionClassResource;
  35. use Symfony\Component\Config\Resource\ResourceInterface;
  36. use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
  37. use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
  38. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  39. use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
  40. use Symfony\Component\ExpressionLanguage\Expression;
  41. use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
  42. /**
  43. * ContainerBuilder is a DI container that provides an API to easily describe services.
  44. *
  45. * @author Fabien Potencier <fabien@symfony.com>
  46. */
  47. class ContainerBuilder extends Container implements TaggedContainerInterface
  48. {
  49. /**
  50. * @var ExtensionInterface[]
  51. */
  52. private $extensions = array();
  53. /**
  54. * @var ExtensionInterface[]
  55. */
  56. private $extensionsByNs = array();
  57. /**
  58. * @var Definition[]
  59. */
  60. private $definitions = array();
  61. /**
  62. * @var Alias[]
  63. */
  64. private $aliasDefinitions = array();
  65. /**
  66. * @var ResourceInterface[]
  67. */
  68. private $resources = array();
  69. private $extensionConfigs = array();
  70. /**
  71. * @var Compiler
  72. */
  73. private $compiler;
  74. private $trackResources;
  75. /**
  76. * @var InstantiatorInterface|null
  77. */
  78. private $proxyInstantiator;
  79. /**
  80. * @var ExpressionLanguage|null
  81. */
  82. private $expressionLanguage;
  83. /**
  84. * @var ExpressionFunctionProviderInterface[]
  85. */
  86. private $expressionLanguageProviders = array();
  87. /**
  88. * @var string[] with tag names used by findTaggedServiceIds
  89. */
  90. private $usedTags = array();
  91. /**
  92. * @var string[][] a map of env var names to their placeholders
  93. */
  94. private $envPlaceholders = array();
  95. /**
  96. * @var int[] a map of env vars to their resolution counter
  97. */
  98. private $envCounters = array();
  99. /**
  100. * @var string[] the list of vendor directories
  101. */
  102. private $vendors;
  103. private $autoconfiguredInstanceof = array();
  104. public function __construct(ParameterBagInterface $parameterBag = null)
  105. {
  106. parent::__construct($parameterBag);
  107. $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
  108. $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true));
  109. $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false));
  110. $this->setAlias(ContainerInterface::class, new Alias('service_container', false));
  111. }
  112. /**
  113. * @var \ReflectionClass[] a list of class reflectors
  114. */
  115. private $classReflectors;
  116. /**
  117. * Sets the track resources flag.
  118. *
  119. * If you are not using the loaders and therefore don't want
  120. * to depend on the Config component, set this flag to false.
  121. *
  122. * @param bool $track True if you want to track resources, false otherwise
  123. */
  124. public function setResourceTracking($track)
  125. {
  126. $this->trackResources = (bool) $track;
  127. }
  128. /**
  129. * Checks if resources are tracked.
  130. *
  131. * @return bool true If resources are tracked, false otherwise
  132. */
  133. public function isTrackingResources()
  134. {
  135. return $this->trackResources;
  136. }
  137. /**
  138. * Sets the instantiator to be used when fetching proxies.
  139. */
  140. public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
  141. {
  142. $this->proxyInstantiator = $proxyInstantiator;
  143. }
  144. public function registerExtension(ExtensionInterface $extension)
  145. {
  146. $this->extensions[$extension->getAlias()] = $extension;
  147. if (false !== $extension->getNamespace()) {
  148. $this->extensionsByNs[$extension->getNamespace()] = $extension;
  149. }
  150. }
  151. /**
  152. * Returns an extension by alias or namespace.
  153. *
  154. * @param string $name An alias or a namespace
  155. *
  156. * @return ExtensionInterface An extension instance
  157. *
  158. * @throws LogicException if the extension is not registered
  159. */
  160. public function getExtension($name)
  161. {
  162. if (isset($this->extensions[$name])) {
  163. return $this->extensions[$name];
  164. }
  165. if (isset($this->extensionsByNs[$name])) {
  166. return $this->extensionsByNs[$name];
  167. }
  168. throw new LogicException(sprintf('Container extension "%s" is not registered', $name));
  169. }
  170. /**
  171. * Returns all registered extensions.
  172. *
  173. * @return ExtensionInterface[] An array of ExtensionInterface
  174. */
  175. public function getExtensions()
  176. {
  177. return $this->extensions;
  178. }
  179. /**
  180. * Checks if we have an extension.
  181. *
  182. * @param string $name The name of the extension
  183. *
  184. * @return bool If the extension exists
  185. */
  186. public function hasExtension($name)
  187. {
  188. return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
  189. }
  190. /**
  191. * Returns an array of resources loaded to build this configuration.
  192. *
  193. * @return ResourceInterface[] An array of resources
  194. */
  195. public function getResources()
  196. {
  197. return array_values($this->resources);
  198. }
  199. /**
  200. * @return $this
  201. */
  202. public function addResource(ResourceInterface $resource)
  203. {
  204. if (!$this->trackResources) {
  205. return $this;
  206. }
  207. if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
  208. return $this;
  209. }
  210. $this->resources[(string) $resource] = $resource;
  211. return $this;
  212. }
  213. /**
  214. * Sets the resources for this configuration.
  215. *
  216. * @param ResourceInterface[] $resources An array of resources
  217. *
  218. * @return $this
  219. */
  220. public function setResources(array $resources)
  221. {
  222. if (!$this->trackResources) {
  223. return $this;
  224. }
  225. $this->resources = $resources;
  226. return $this;
  227. }
  228. /**
  229. * Adds the object class hierarchy as resources.
  230. *
  231. * @param object|string $object An object instance or class name
  232. *
  233. * @return $this
  234. */
  235. public function addObjectResource($object)
  236. {
  237. if ($this->trackResources) {
  238. if (is_object($object)) {
  239. $object = get_class($object);
  240. }
  241. if (!isset($this->classReflectors[$object])) {
  242. $this->classReflectors[$object] = new \ReflectionClass($object);
  243. }
  244. $class = $this->classReflectors[$object];
  245. foreach ($class->getInterfaceNames() as $name) {
  246. if (null === $interface = &$this->classReflectors[$name]) {
  247. $interface = new \ReflectionClass($name);
  248. }
  249. $file = $interface->getFileName();
  250. if (false !== $file && file_exists($file)) {
  251. $this->fileExists($file);
  252. }
  253. }
  254. do {
  255. $file = $class->getFileName();
  256. if (false !== $file && file_exists($file)) {
  257. $this->fileExists($file);
  258. }
  259. foreach ($class->getTraitNames() as $name) {
  260. $this->addObjectResource($name);
  261. }
  262. } while ($class = $class->getParentClass());
  263. }
  264. return $this;
  265. }
  266. /**
  267. * Adds the given class hierarchy as resources.
  268. *
  269. * @return $this
  270. *
  271. * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead.
  272. */
  273. public function addClassResource(\ReflectionClass $class)
  274. {
  275. @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the addObjectResource() or the getReflectionClass() method instead.', E_USER_DEPRECATED);
  276. return $this->addObjectResource($class->name);
  277. }
  278. /**
  279. * Retrieves the requested reflection class and registers it for resource tracking.
  280. *
  281. * @param string $class
  282. * @param bool $throw
  283. *
  284. * @return \ReflectionClass|null
  285. *
  286. * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
  287. *
  288. * @final
  289. */
  290. public function getReflectionClass($class, $throw = true)
  291. {
  292. if (!$class = $this->getParameterBag()->resolveValue($class)) {
  293. return;
  294. }
  295. $resource = null;
  296. try {
  297. if (isset($this->classReflectors[$class])) {
  298. $classReflector = $this->classReflectors[$class];
  299. } else {
  300. $resource = new ClassExistenceResource($class, false);
  301. $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
  302. }
  303. } catch (\ReflectionException $e) {
  304. if ($throw) {
  305. throw $e;
  306. }
  307. $classReflector = false;
  308. }
  309. if ($this->trackResources) {
  310. if (!$classReflector) {
  311. $this->addResource($resource ?: new ClassExistenceResource($class, false));
  312. } elseif (!$classReflector->isInternal()) {
  313. $path = $classReflector->getFileName();
  314. if (!$this->inVendors($path)) {
  315. $this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
  316. }
  317. }
  318. $this->classReflectors[$class] = $classReflector;
  319. }
  320. return $classReflector ?: null;
  321. }
  322. /**
  323. * Checks whether the requested file or directory exists and registers the result for resource tracking.
  324. *
  325. * @param string $path The file or directory path for which to check the existence
  326. * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
  327. * it will be used as pattern for tracking contents of the requested directory
  328. *
  329. * @return bool
  330. *
  331. * @final
  332. */
  333. public function fileExists($path, $trackContents = true)
  334. {
  335. $exists = file_exists($path);
  336. if (!$this->trackResources || $this->inVendors($path)) {
  337. return $exists;
  338. }
  339. if (!$exists) {
  340. $this->addResource(new FileExistenceResource($path));
  341. return $exists;
  342. }
  343. if (is_dir($path)) {
  344. if ($trackContents) {
  345. $this->addResource(new DirectoryResource($path, is_string($trackContents) ? $trackContents : null));
  346. } else {
  347. $this->addResource(new GlobResource($path, '/*', false));
  348. }
  349. } elseif ($trackContents) {
  350. $this->addResource(new FileResource($path));
  351. }
  352. return $exists;
  353. }
  354. /**
  355. * Loads the configuration for an extension.
  356. *
  357. * @param string $extension The extension alias or namespace
  358. * @param array $values An array of values that customizes the extension
  359. *
  360. * @return $this
  361. *
  362. * @throws BadMethodCallException When this ContainerBuilder is compiled
  363. * @throws \LogicException if the extension is not registered
  364. */
  365. public function loadFromExtension($extension, array $values = null)
  366. {
  367. if ($this->isCompiled()) {
  368. throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
  369. }
  370. if (func_num_args() < 2) {
  371. $values = array();
  372. }
  373. $namespace = $this->getExtension($extension)->getAlias();
  374. $this->extensionConfigs[$namespace][] = $values;
  375. return $this;
  376. }
  377. /**
  378. * Adds a compiler pass.
  379. *
  380. * @param CompilerPassInterface $pass A compiler pass
  381. * @param string $type The type of compiler pass
  382. * @param int $priority Used to sort the passes
  383. *
  384. * @return $this
  385. */
  386. public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
  387. {
  388. if (func_num_args() >= 3) {
  389. $priority = func_get_arg(2);
  390. } else {
  391. if (__CLASS__ !== get_class($this)) {
  392. $r = new \ReflectionMethod($this, __FUNCTION__);
  393. if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
  394. @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED);
  395. }
  396. }
  397. $priority = 0;
  398. }
  399. $this->getCompiler()->addPass($pass, $type, $priority);
  400. $this->addObjectResource($pass);
  401. return $this;
  402. }
  403. /**
  404. * Returns the compiler pass config which can then be modified.
  405. *
  406. * @return PassConfig The compiler pass config
  407. */
  408. public function getCompilerPassConfig()
  409. {
  410. return $this->getCompiler()->getPassConfig();
  411. }
  412. /**
  413. * Returns the compiler.
  414. *
  415. * @return Compiler The compiler
  416. */
  417. public function getCompiler()
  418. {
  419. if (null === $this->compiler) {
  420. $this->compiler = new Compiler();
  421. }
  422. return $this->compiler;
  423. }
  424. /**
  425. * Sets a service.
  426. *
  427. * @param string $id The service identifier
  428. * @param object $service The service instance
  429. *
  430. * @throws BadMethodCallException When this ContainerBuilder is compiled
  431. */
  432. public function set($id, $service)
  433. {
  434. $id = $this->normalizeId($id);
  435. if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
  436. // setting a synthetic service on a compiled container is alright
  437. throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
  438. }
  439. unset($this->definitions[$id], $this->aliasDefinitions[$id]);
  440. parent::set($id, $service);
  441. }
  442. /**
  443. * Removes a service definition.
  444. *
  445. * @param string $id The service identifier
  446. */
  447. public function removeDefinition($id)
  448. {
  449. unset($this->definitions[$this->normalizeId($id)]);
  450. }
  451. /**
  452. * Returns true if the given service is defined.
  453. *
  454. * @param string $id The service identifier
  455. *
  456. * @return bool true if the service is defined, false otherwise
  457. */
  458. public function has($id)
  459. {
  460. $id = $this->normalizeId($id);
  461. return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
  462. }
  463. /**
  464. * Gets a service.
  465. *
  466. * @param string $id The service identifier
  467. * @param int $invalidBehavior The behavior when the service does not exist
  468. *
  469. * @return object The associated service
  470. *
  471. * @throws InvalidArgumentException when no definitions are available
  472. * @throws ServiceCircularReferenceException When a circular reference is detected
  473. * @throws ServiceNotFoundException When the service is not defined
  474. * @throws \Exception
  475. *
  476. * @see Reference
  477. */
  478. public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
  479. {
  480. $id = $this->normalizeId($id);
  481. if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
  482. return $service;
  483. }
  484. if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
  485. return $this->get((string) $this->aliasDefinitions[$id], $invalidBehavior);
  486. }
  487. try {
  488. $definition = $this->getDefinition($id);
  489. } catch (ServiceNotFoundException $e) {
  490. if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
  491. return;
  492. }
  493. throw $e;
  494. }
  495. $this->loading[$id] = true;
  496. try {
  497. $service = $this->createService($definition, new \SplObjectStorage(), $id);
  498. } finally {
  499. unset($this->loading[$id]);
  500. }
  501. return $service;
  502. }
  503. /**
  504. * Merges a ContainerBuilder with the current ContainerBuilder configuration.
  505. *
  506. * Service definitions overrides the current defined ones.
  507. *
  508. * But for parameters, they are overridden by the current ones. It allows
  509. * the parameters passed to the container constructor to have precedence
  510. * over the loaded ones.
  511. *
  512. * $container = new ContainerBuilder(array('foo' => 'bar'));
  513. * $loader = new LoaderXXX($container);
  514. * $loader->load('resource_name');
  515. * $container->register('foo', new stdClass());
  516. *
  517. * In the above example, even if the loaded resource defines a foo
  518. * parameter, the value will still be 'bar' as defined in the ContainerBuilder
  519. * constructor.
  520. *
  521. * @throws BadMethodCallException When this ContainerBuilder is compiled
  522. */
  523. public function merge(ContainerBuilder $container)
  524. {
  525. if ($this->isCompiled()) {
  526. throw new BadMethodCallException('Cannot merge on a compiled container.');
  527. }
  528. $this->addDefinitions($container->getDefinitions());
  529. $this->addAliases($container->getAliases());
  530. $this->getParameterBag()->add($container->getParameterBag()->all());
  531. if ($this->trackResources) {
  532. foreach ($container->getResources() as $resource) {
  533. $this->addResource($resource);
  534. }
  535. }
  536. foreach ($this->extensions as $name => $extension) {
  537. if (!isset($this->extensionConfigs[$name])) {
  538. $this->extensionConfigs[$name] = array();
  539. }
  540. $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
  541. }
  542. if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
  543. $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
  544. $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
  545. } else {
  546. $envPlaceholders = array();
  547. }
  548. foreach ($container->envCounters as $env => $count) {
  549. if (!$count && !isset($envPlaceholders[$env])) {
  550. continue;
  551. }
  552. if (!isset($this->envCounters[$env])) {
  553. $this->envCounters[$env] = $count;
  554. } else {
  555. $this->envCounters[$env] += $count;
  556. }
  557. }
  558. foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
  559. if (isset($this->autoconfiguredInstanceof[$interface])) {
  560. throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface));
  561. }
  562. $this->autoconfiguredInstanceof[$interface] = $childDefinition;
  563. }
  564. }
  565. /**
  566. * Returns the configuration array for the given extension.
  567. *
  568. * @param string $name The name of the extension
  569. *
  570. * @return array An array of configuration
  571. */
  572. public function getExtensionConfig($name)
  573. {
  574. if (!isset($this->extensionConfigs[$name])) {
  575. $this->extensionConfigs[$name] = array();
  576. }
  577. return $this->extensionConfigs[$name];
  578. }
  579. /**
  580. * Prepends a config array to the configs of the given extension.
  581. *
  582. * @param string $name The name of the extension
  583. * @param array $config The config to set
  584. */
  585. public function prependExtensionConfig($name, array $config)
  586. {
  587. if (!isset($this->extensionConfigs[$name])) {
  588. $this->extensionConfigs[$name] = array();
  589. }
  590. array_unshift($this->extensionConfigs[$name], $config);
  591. }
  592. /**
  593. * Compiles the container.
  594. *
  595. * This method passes the container to compiler
  596. * passes whose job is to manipulate and optimize
  597. * the container.
  598. *
  599. * The main compiler passes roughly do four things:
  600. *
  601. * * The extension configurations are merged;
  602. * * Parameter values are resolved;
  603. * * The parameter bag is frozen;
  604. * * Extension loading is disabled.
  605. *
  606. * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
  607. * env vars or be replaced by uniquely identifiable placeholders.
  608. * Set to "true" when you want to use the current ContainerBuilder
  609. * directly, keep to "false" when the container is dumped instead.
  610. */
  611. public function compile(/*$resolveEnvPlaceholders = false*/)
  612. {
  613. if (1 <= func_num_args()) {
  614. $resolveEnvPlaceholders = func_get_arg(0);
  615. } else {
  616. if (__CLASS__ !== static::class) {
  617. $r = new \ReflectionMethod($this, __FUNCTION__);
  618. if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) {
  619. @trigger_error(sprintf('The %s::compile() method expects a first "$resolveEnvPlaceholders" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), E_USER_DEPRECATED);
  620. }
  621. }
  622. $resolveEnvPlaceholders = false;
  623. }
  624. $compiler = $this->getCompiler();
  625. if ($this->trackResources) {
  626. foreach ($compiler->getPassConfig()->getPasses() as $pass) {
  627. $this->addObjectResource($pass);
  628. }
  629. }
  630. $bag = $this->getParameterBag();
  631. if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
  632. $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
  633. }
  634. $compiler->compile($this);
  635. foreach ($this->definitions as $id => $definition) {
  636. if ($this->trackResources && $definition->isLazy()) {
  637. $this->getReflectionClass($definition->getClass());
  638. }
  639. }
  640. $this->extensionConfigs = array();
  641. if ($bag instanceof EnvPlaceholderParameterBag) {
  642. if ($resolveEnvPlaceholders) {
  643. $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
  644. }
  645. $this->envPlaceholders = $bag->getEnvPlaceholders();
  646. }
  647. parent::compile();
  648. }
  649. /**
  650. * Gets all service ids.
  651. *
  652. * @return array An array of all defined service ids
  653. */
  654. public function getServiceIds()
  655. {
  656. return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()));
  657. }
  658. /**
  659. * Adds the service aliases.
  660. */
  661. public function addAliases(array $aliases)
  662. {
  663. foreach ($aliases as $alias => $id) {
  664. $this->setAlias($alias, $id);
  665. }
  666. }
  667. /**
  668. * Sets the service aliases.
  669. */
  670. public function setAliases(array $aliases)
  671. {
  672. $this->aliasDefinitions = array();
  673. $this->addAliases($aliases);
  674. }
  675. /**
  676. * Sets an alias for an existing service.
  677. *
  678. * @param string $alias The alias to create
  679. * @param string|Alias $id The service to alias
  680. *
  681. * @throws InvalidArgumentException if the id is not a string or an Alias
  682. * @throws InvalidArgumentException if the alias is for itself
  683. */
  684. public function setAlias($alias, $id)
  685. {
  686. $alias = $this->normalizeId($alias);
  687. if (is_string($id)) {
  688. $id = new Alias($this->normalizeId($id));
  689. } elseif (!$id instanceof Alias) {
  690. throw new InvalidArgumentException('$id must be a string, or an Alias object.');
  691. }
  692. if ($alias === (string) $id) {
  693. throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
  694. }
  695. unset($this->definitions[$alias]);
  696. $this->aliasDefinitions[$alias] = $id;
  697. }
  698. /**
  699. * Removes an alias.
  700. *
  701. * @param string $alias The alias to remove
  702. */
  703. public function removeAlias($alias)
  704. {
  705. unset($this->aliasDefinitions[$this->normalizeId($alias)]);
  706. }
  707. /**
  708. * Returns true if an alias exists under the given identifier.
  709. *
  710. * @param string $id The service identifier
  711. *
  712. * @return bool true if the alias exists, false otherwise
  713. */
  714. public function hasAlias($id)
  715. {
  716. return isset($this->aliasDefinitions[$this->normalizeId($id)]);
  717. }
  718. /**
  719. * Gets all defined aliases.
  720. *
  721. * @return Alias[] An array of aliases
  722. */
  723. public function getAliases()
  724. {
  725. return $this->aliasDefinitions;
  726. }
  727. /**
  728. * Gets an alias.
  729. *
  730. * @param string $id The service identifier
  731. *
  732. * @return Alias An Alias instance
  733. *
  734. * @throws InvalidArgumentException if the alias does not exist
  735. */
  736. public function getAlias($id)
  737. {
  738. $id = $this->normalizeId($id);
  739. if (!isset($this->aliasDefinitions[$id])) {
  740. throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
  741. }
  742. return $this->aliasDefinitions[$id];
  743. }
  744. /**
  745. * Registers a service definition.
  746. *
  747. * This methods allows for simple registration of service definition
  748. * with a fluid interface.
  749. *
  750. * @param string $id The service identifier
  751. * @param string $class|null The service class
  752. *
  753. * @return Definition A Definition instance
  754. */
  755. public function register($id, $class = null)
  756. {
  757. return $this->setDefinition($id, new Definition($class));
  758. }
  759. /**
  760. * Registers an autowired service definition.
  761. *
  762. * This method implements a shortcut for using setDefinition() with
  763. * an autowired definition.
  764. *
  765. * @param string $id The service identifier
  766. * @param null|string $class The service class
  767. *
  768. * @return Definition The created definition
  769. */
  770. public function autowire($id, $class = null)
  771. {
  772. return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
  773. }
  774. /**
  775. * Adds the service definitions.
  776. *
  777. * @param Definition[] $definitions An array of service definitions
  778. */
  779. public function addDefinitions(array $definitions)
  780. {
  781. foreach ($definitions as $id => $definition) {
  782. $this->setDefinition($id, $definition);
  783. }
  784. }
  785. /**
  786. * Sets the service definitions.
  787. *
  788. * @param Definition[] $definitions An array of service definitions
  789. */
  790. public function setDefinitions(array $definitions)
  791. {
  792. $this->definitions = array();
  793. $this->addDefinitions($definitions);
  794. }
  795. /**
  796. * Gets all service definitions.
  797. *
  798. * @return Definition[] An array of Definition instances
  799. */
  800. public function getDefinitions()
  801. {
  802. return $this->definitions;
  803. }
  804. /**
  805. * Sets a service definition.
  806. *
  807. * @param string $id The service identifier
  808. * @param Definition $definition A Definition instance
  809. *
  810. * @return Definition the service definition
  811. *
  812. * @throws BadMethodCallException When this ContainerBuilder is compiled
  813. */
  814. public function setDefinition($id, Definition $definition)
  815. {
  816. if ($this->isCompiled()) {
  817. throw new BadMethodCallException('Adding definition to a compiled container is not allowed');
  818. }
  819. $id = $this->normalizeId($id);
  820. unset($this->aliasDefinitions[$id]);
  821. return $this->definitions[$id] = $definition;
  822. }
  823. /**
  824. * Returns true if a service definition exists under the given identifier.
  825. *
  826. * @param string $id The service identifier
  827. *
  828. * @return bool true if the service definition exists, false otherwise
  829. */
  830. public function hasDefinition($id)
  831. {
  832. return isset($this->definitions[$this->normalizeId($id)]);
  833. }
  834. /**
  835. * Gets a service definition.
  836. *
  837. * @param string $id The service identifier
  838. *
  839. * @return Definition A Definition instance
  840. *
  841. * @throws ServiceNotFoundException if the service definition does not exist
  842. */
  843. public function getDefinition($id)
  844. {
  845. $id = $this->normalizeId($id);
  846. if (!isset($this->definitions[$id])) {
  847. throw new ServiceNotFoundException($id);
  848. }
  849. return $this->definitions[$id];
  850. }
  851. /**
  852. * Gets a service definition by id or alias.
  853. *
  854. * The method "unaliases" recursively to return a Definition instance.
  855. *
  856. * @param string $id The service identifier or alias
  857. *
  858. * @return Definition A Definition instance
  859. *
  860. * @throws ServiceNotFoundException if the service definition does not exist
  861. */
  862. public function findDefinition($id)
  863. {
  864. $id = $this->normalizeId($id);
  865. $seen = array();
  866. while (isset($this->aliasDefinitions[$id])) {
  867. $id = (string) $this->aliasDefinitions[$id];
  868. if (isset($seen[$id])) {
  869. $seen = array_values($seen);
  870. $seen = array_slice($seen, array_search($id, $seen));
  871. $seen[] = $id;
  872. throw new ServiceCircularReferenceException($id, $seen);
  873. }
  874. $seen[$id] = $id;
  875. }
  876. return $this->getDefinition($id);
  877. }
  878. /**
  879. * Creates a service for a service definition.
  880. *
  881. * @param Definition $definition A service definition instance
  882. * @param string $id The service identifier
  883. * @param bool $tryProxy Whether to try proxying the service with a lazy proxy
  884. *
  885. * @return object The service described by the service definition
  886. *
  887. * @throws RuntimeException When the factory definition is incomplete
  888. * @throws RuntimeException When the service is a synthetic service
  889. * @throws InvalidArgumentException When configure callable is not callable
  890. */
  891. private function createService(Definition $definition, \SplObjectStorage $inlinedDefinitions, $id = null, $tryProxy = true)
  892. {
  893. if (null === $id && isset($inlinedDefinitions[$definition])) {
  894. return $inlinedDefinitions[$definition];
  895. }
  896. if ($definition instanceof ChildDefinition) {
  897. throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id));
  898. }
  899. if ($definition->isSynthetic()) {
  900. throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
  901. }
  902. if ($definition->isDeprecated()) {
  903. @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED);
  904. }
  905. if ($tryProxy && $definition->isLazy()) {
  906. $proxy = $this
  907. ->getProxyInstantiator()
  908. ->instantiateProxy(
  909. $this,
  910. $definition,
  911. $id, function () use ($definition, $inlinedDefinitions, $id) {
  912. return $this->createService($definition, $inlinedDefinitions, $id, false);
  913. }
  914. );
  915. $this->shareService($definition, $proxy, $id, $inlinedDefinitions);
  916. return $proxy;
  917. }
  918. $parameterBag = $this->getParameterBag();
  919. if (null !== $definition->getFile()) {
  920. require_once $parameterBag->resolveValue($definition->getFile());
  921. }
  922. $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlinedDefinitions);
  923. if (null !== $factory = $definition->getFactory()) {
  924. if (is_array($factory)) {
  925. $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlinedDefinitions), $factory[1]);
  926. } elseif (!is_string($factory)) {
  927. throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
  928. }
  929. $service = call_user_func_array($factory, $arguments);
  930. if (!$definition->isDeprecated() && is_array($factory) && is_string($factory[0])) {
  931. $r = new \ReflectionClass($factory[0]);
  932. if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
  933. @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED);
  934. }
  935. }
  936. } else {
  937. $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass()));
  938. $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
  939. // don't trigger deprecations for internal uses
  940. // @deprecated since version 3.3, to be removed in 4.0 along with the deprecated class
  941. $deprecationWhitelist = array('event_dispatcher' => ContainerAwareEventDispatcher::class);
  942. if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationWhitelist[$id]) || $deprecationWhitelist[$id] !== $class)) {
  943. @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED);
  944. }
  945. }
  946. if ($tryProxy || !$definition->isLazy()) {
  947. // share only if proxying failed, or if not a proxy
  948. $this->shareService($definition, $service, $id, $inlinedDefinitions);
  949. }
  950. $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlinedDefinitions);
  951. foreach ($properties as $name => $value) {
  952. $service->$name = $value;
  953. }
  954. foreach ($definition->getMethodCalls() as $call) {
  955. $this->callMethod($service, $call, $inlinedDefinitions);
  956. }
  957. if ($callable = $definition->getConfigurator()) {
  958. if (is_array($callable)) {
  959. $callable[0] = $parameterBag->resolveValue($callable[0]);
  960. if ($callable[0] instanceof Reference) {
  961. $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior());
  962. } elseif ($callable[0] instanceof Definition) {
  963. $callable[0] = $this->createService($callable[0], $inlinedDefinitions);
  964. }
  965. }
  966. if (!is_callable($callable)) {
  967. throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service)));
  968. }
  969. call_user_func($callable, $service);
  970. }
  971. return $service;
  972. }
  973. /**
  974. * Replaces service references by the real service instance and evaluates expressions.
  975. *
  976. * @param mixed $value A value
  977. *
  978. * @return mixed The same value with all service references replaced by
  979. * the real service instances and all expressions evaluated
  980. */
  981. public function resolveServices($value)
  982. {
  983. return $this->doResolveServices($value, new \SplObjectStorage());
  984. }
  985. private function doResolveServices($value, \SplObjectStorage $inlinedDefinitions)
  986. {
  987. if (is_array($value)) {
  988. foreach ($value as $k => $v) {
  989. $value[$k] = $this->doResolveServices($v, $inlinedDefinitions);
  990. }
  991. } elseif ($value instanceof ServiceClosureArgument) {
  992. $reference = $value->getValues()[0];
  993. $value = function () use ($reference) {
  994. return $this->resolveServices($reference);
  995. };
  996. } elseif ($value instanceof IteratorArgument) {
  997. $value = new RewindableGenerator(function () use ($value) {
  998. foreach ($value->getValues() as $k => $v) {
  999. foreach (self::getServiceConditionals($v) as $s) {
  1000. if (!$this->has($s)) {
  1001. continue 2;
  1002. }
  1003. }
  1004. yield $k => $this->resolveServices($v);
  1005. }
  1006. }, function () use ($value) {
  1007. $count = 0;
  1008. foreach ($value->getValues() as $v) {
  1009. foreach (self::getServiceConditionals($v) as $s) {
  1010. if (!$this->has($s)) {
  1011. continue 2;
  1012. }
  1013. }
  1014. ++$count;
  1015. }
  1016. return $count;
  1017. });
  1018. } elseif ($value instanceof Reference) {
  1019. $value = $this->get((string) $value, $value->getInvalidBehavior());
  1020. } elseif ($value instanceof Definition) {
  1021. $value = $this->createService($value, $inlinedDefinitions);
  1022. } elseif ($value instanceof Expression) {
  1023. $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this));
  1024. }
  1025. return $value;
  1026. }
  1027. /**
  1028. * Returns service ids for a given tag.
  1029. *
  1030. * Example:
  1031. *
  1032. * $container->register('foo')->addTag('my.tag', array('hello' => 'world'));
  1033. *
  1034. * $serviceIds = $container->findTaggedServiceIds('my.tag');
  1035. * foreach ($serviceIds as $serviceId => $tags) {
  1036. * foreach ($tags as $tag) {
  1037. * echo $tag['hello'];
  1038. * }
  1039. * }
  1040. *
  1041. * @param string $name
  1042. * @param bool $throwOnAbstract
  1043. *
  1044. * @return array An array of tags with the tagged service as key, holding a list of attribute arrays
  1045. */
  1046. public function findTaggedServiceIds($name, $throwOnAbstract = false)
  1047. {
  1048. $this->usedTags[] = $name;
  1049. $tags = array();
  1050. foreach ($this->getDefinitions() as $id => $definition) {
  1051. if ($definition->hasTag($name)) {
  1052. if ($throwOnAbstract && $definition->isAbstract()) {
  1053. throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
  1054. }
  1055. $tags[$id] = $definition->getTag($name);
  1056. }
  1057. }
  1058. return $tags;
  1059. }
  1060. /**
  1061. * Returns all tags the defined services use.
  1062. *
  1063. * @return array An array of tags
  1064. */
  1065. public function findTags()
  1066. {
  1067. $tags = array();
  1068. foreach ($this->getDefinitions() as $id => $definition) {
  1069. $tags = array_merge(array_keys($definition->getTags()), $tags);
  1070. }
  1071. return array_unique($tags);
  1072. }
  1073. /**
  1074. * Returns all tags not queried by findTaggedServiceIds.
  1075. *
  1076. * @return string[] An array of tags
  1077. */
  1078. public function findUnusedTags()
  1079. {
  1080. return array_values(array_diff($this->findTags(), $this->usedTags));
  1081. }
  1082. public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
  1083. {
  1084. $this->expressionLanguageProviders[] = $provider;
  1085. }
  1086. /**
  1087. * @return ExpressionFunctionProviderInterface[]
  1088. */
  1089. public function getExpressionLanguageProviders()
  1090. {
  1091. return $this->expressionLanguageProviders;
  1092. }
  1093. /**
  1094. * Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
  1095. *
  1096. * @param string $interface The class or interface to match
  1097. *
  1098. * @return ChildDefinition
  1099. */
  1100. public function registerForAutoconfiguration($interface)
  1101. {
  1102. if (!isset($this->autoconfiguredInstanceof[$interface])) {
  1103. $this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
  1104. }
  1105. return $this->autoconfiguredInstanceof[$interface];
  1106. }
  1107. /**
  1108. * Returns an array of ChildDefinition[] keyed by interface.
  1109. *
  1110. * @return ChildDefinition[]
  1111. */
  1112. public function getAutoconfiguredInstanceof()
  1113. {
  1114. return $this->autoconfiguredInstanceof;
  1115. }
  1116. /**
  1117. * Resolves env parameter placeholders in a string or an array.
  1118. *
  1119. * @param mixed $value The value to resolve
  1120. * @param string|true|null $format A sprintf() format returning the replacement for each env var name or
  1121. * null to resolve back to the original "%env(VAR)%" format or
  1122. * true to resolve to the actual values of the referenced env vars
  1123. * @param array &$usedEnvs Env vars found while resolving are added to this array
  1124. *
  1125. * @return mixed The value with env parameters resolved if a string or an array is passed
  1126. */
  1127. public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
  1128. {
  1129. if (null === $format) {
  1130. $format = '%%env(%s)%%';
  1131. }
  1132. $bag = $this->getParameterBag();
  1133. if (true === $format) {
  1134. $value = $bag->resolveValue($value);
  1135. }
  1136. if (is_array($value)) {
  1137. $result = array();
  1138. foreach ($value as $k => $v) {
  1139. $result[$this->resolveEnvPlaceholders($k, $format, $usedEnvs)] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs);
  1140. }
  1141. return $result;
  1142. }
  1143. if (!is_string($value)) {
  1144. return $value;
  1145. }
  1146. $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
  1147. foreach ($envPlaceholders as $env => $placeholders) {
  1148. foreach ($placeholders as $placeholder) {
  1149. if (false !== stripos($value, $placeholder)) {
  1150. if (true === $format) {
  1151. $resolved = $bag->escapeValue($this->getEnv($env));
  1152. } else {
  1153. $resolved = sprintf($format, $env);
  1154. }
  1155. if ($placeholder === $value) {
  1156. $value = $resolved;
  1157. } else {
  1158. if (!is_string($resolved) && !is_numeric($resolved)) {
  1159. throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type %s inside string value "%s".', $env, gettype($resolved), $value));
  1160. }
  1161. $value = str_ireplace($placeholder, $resolved, $value);
  1162. }
  1163. $usedEnvs[$env] = $env;
  1164. $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1;
  1165. }
  1166. }
  1167. }
  1168. return $value;
  1169. }
  1170. /**
  1171. * Get statistics about env usage.
  1172. *
  1173. * @return int[] The number of time each env vars has been resolved
  1174. */
  1175. public function getEnvCounters()
  1176. {
  1177. $bag = $this->getParameterBag();
  1178. $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
  1179. foreach ($envPlaceholders as $env => $placeholders) {
  1180. if (!isset($this->envCounters[$env])) {
  1181. $this->envCounters[$env] = 0;
  1182. }
  1183. }
  1184. return $this->envCounters;
  1185. }
  1186. /**
  1187. * @internal
  1188. */
  1189. public function getNormalizedIds()
  1190. {
  1191. $normalizedIds = array();
  1192. foreach ($this->normalizedIds as $k => $v) {
  1193. if ($v !== (string) $k) {
  1194. $normalizedIds[$k] = $v;
  1195. }
  1196. }
  1197. return $normalizedIds;
  1198. }
  1199. /**
  1200. * @final
  1201. */
  1202. public function log(CompilerPassInterface $pass, $message)
  1203. {
  1204. $this->getCompiler()->log($pass, $message);
  1205. }
  1206. /**
  1207. * Returns the Service Conditionals.
  1208. *
  1209. * @param mixed $value An array of conditionals to return
  1210. *
  1211. * @return array An array of Service conditionals
  1212. */
  1213. public static function getServiceConditionals($value)
  1214. {
  1215. $services = array();
  1216. if (is_array($value)) {
  1217. foreach ($value as $v) {
  1218. $services = array_unique(array_merge($services, self::getServiceConditionals($v)));
  1219. }
  1220. } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
  1221. $services[] = (string) $value;
  1222. }
  1223. return $services;
  1224. }
  1225. /**
  1226. * {@inheritdoc}
  1227. */
  1228. protected function getEnv($name)
  1229. {
  1230. $value = parent::getEnv($name);
  1231. if (!is_string($value) || !$this->getParameterBag() instanceof EnvPlaceholderParameterBag) {
  1232. return $value;
  1233. }
  1234. foreach ($this->getParameterBag()->getEnvPlaceholders() as $env => $placeholders) {
  1235. if (isset($placeholders[$value])) {
  1236. $bag = new ParameterBag($this->getParameterBag()->all());
  1237. return $bag->unescapeValue($bag->get("env($name)"));
  1238. }
  1239. }
  1240. return $value;
  1241. }
  1242. /**
  1243. * Retrieves the currently set proxy instantiator or instantiates one.
  1244. *
  1245. * @return InstantiatorInterface
  1246. */
  1247. private function getProxyInstantiator()
  1248. {
  1249. if (!$this->proxyInstantiator) {
  1250. $this->proxyInstantiator = new RealServiceInstantiator();
  1251. }
  1252. return $this->proxyInstantiator;
  1253. }
  1254. private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitions)
  1255. {
  1256. $services = self::getServiceConditionals($call[1]);
  1257. foreach ($services as $s) {
  1258. if (!$this->has($s)) {
  1259. return;
  1260. }
  1261. }
  1262. call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlinedDefinitions));
  1263. }
  1264. /**
  1265. * Shares a given service in the container.
  1266. *
  1267. * @param Definition $definition
  1268. * @param object $service
  1269. * @param string|null $id
  1270. */
  1271. private function shareService(Definition $definition, $service, $id, \SplObjectStorage $inlinedDefinitions)
  1272. {
  1273. if (!$definition->isShared()) {
  1274. return;
  1275. }
  1276. if (null === $id) {
  1277. $inlinedDefinitions[$definition] = $service;
  1278. } else {
  1279. $this->services[$this->normalizeId($id)] = $service;
  1280. }
  1281. }
  1282. private function getExpressionLanguage()
  1283. {
  1284. if (null === $this->expressionLanguage) {
  1285. if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
  1286. throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
  1287. }
  1288. $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
  1289. }
  1290. return $this->expressionLanguage;
  1291. }
  1292. private function inVendors($path)
  1293. {
  1294. if (null === $this->vendors) {
  1295. $resource = new ComposerResource();
  1296. $this->vendors = $resource->getVendors();
  1297. $this->addResource($resource);
  1298. }
  1299. $path = realpath($path) ?: $path;
  1300. foreach ($this->vendors as $vendor) {
  1301. if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) {
  1302. return true;
  1303. }
  1304. }
  1305. return false;
  1306. }
  1307. }