Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\DependencyInjection;
|
Chris@0
|
13
|
Chris@14
|
14 use Psr\Container\ContainerInterface as PsrContainerInterface;
|
Chris@17
|
15 use Symfony\Component\Config\Resource\ClassExistenceResource;
|
Chris@17
|
16 use Symfony\Component\Config\Resource\ComposerResource;
|
Chris@17
|
17 use Symfony\Component\Config\Resource\DirectoryResource;
|
Chris@17
|
18 use Symfony\Component\Config\Resource\FileExistenceResource;
|
Chris@17
|
19 use Symfony\Component\Config\Resource\FileResource;
|
Chris@17
|
20 use Symfony\Component\Config\Resource\GlobResource;
|
Chris@17
|
21 use Symfony\Component\Config\Resource\ReflectionClassResource;
|
Chris@17
|
22 use Symfony\Component\Config\Resource\ResourceInterface;
|
Chris@14
|
23 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
Chris@14
|
24 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
Chris@14
|
25 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
Chris@0
|
26 use Symfony\Component\DependencyInjection\Compiler\Compiler;
|
Chris@0
|
27 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
Chris@0
|
28 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
Chris@14
|
29 use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
|
Chris@0
|
30 use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
|
Chris@0
|
31 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
Chris@0
|
32 use Symfony\Component\DependencyInjection\Exception\LogicException;
|
Chris@0
|
33 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
Chris@0
|
34 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
|
Chris@0
|
35 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
Chris@0
|
36 use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
Chris@17
|
37 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
|
Chris@17
|
38 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
|
Chris@0
|
39 use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
Chris@14
|
40 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
Chris@0
|
41 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
Chris@14
|
42 use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
|
Chris@0
|
43 use Symfony\Component\ExpressionLanguage\Expression;
|
Chris@0
|
44 use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
Chris@0
|
45
|
Chris@0
|
46 /**
|
Chris@0
|
47 * ContainerBuilder is a DI container that provides an API to easily describe services.
|
Chris@0
|
48 *
|
Chris@0
|
49 * @author Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
50 */
|
Chris@0
|
51 class ContainerBuilder extends Container implements TaggedContainerInterface
|
Chris@0
|
52 {
|
Chris@0
|
53 /**
|
Chris@0
|
54 * @var ExtensionInterface[]
|
Chris@0
|
55 */
|
Chris@17
|
56 private $extensions = [];
|
Chris@0
|
57
|
Chris@0
|
58 /**
|
Chris@0
|
59 * @var ExtensionInterface[]
|
Chris@0
|
60 */
|
Chris@17
|
61 private $extensionsByNs = [];
|
Chris@0
|
62
|
Chris@0
|
63 /**
|
Chris@0
|
64 * @var Definition[]
|
Chris@0
|
65 */
|
Chris@17
|
66 private $definitions = [];
|
Chris@0
|
67
|
Chris@0
|
68 /**
|
Chris@0
|
69 * @var Alias[]
|
Chris@0
|
70 */
|
Chris@17
|
71 private $aliasDefinitions = [];
|
Chris@0
|
72
|
Chris@0
|
73 /**
|
Chris@0
|
74 * @var ResourceInterface[]
|
Chris@0
|
75 */
|
Chris@17
|
76 private $resources = [];
|
Chris@0
|
77
|
Chris@17
|
78 private $extensionConfigs = [];
|
Chris@0
|
79
|
Chris@0
|
80 /**
|
Chris@0
|
81 * @var Compiler
|
Chris@0
|
82 */
|
Chris@0
|
83 private $compiler;
|
Chris@0
|
84
|
Chris@0
|
85 private $trackResources;
|
Chris@0
|
86
|
Chris@0
|
87 /**
|
Chris@0
|
88 * @var InstantiatorInterface|null
|
Chris@0
|
89 */
|
Chris@0
|
90 private $proxyInstantiator;
|
Chris@0
|
91
|
Chris@0
|
92 /**
|
Chris@0
|
93 * @var ExpressionLanguage|null
|
Chris@0
|
94 */
|
Chris@0
|
95 private $expressionLanguage;
|
Chris@0
|
96
|
Chris@0
|
97 /**
|
Chris@0
|
98 * @var ExpressionFunctionProviderInterface[]
|
Chris@0
|
99 */
|
Chris@17
|
100 private $expressionLanguageProviders = [];
|
Chris@0
|
101
|
Chris@0
|
102 /**
|
Chris@0
|
103 * @var string[] with tag names used by findTaggedServiceIds
|
Chris@0
|
104 */
|
Chris@17
|
105 private $usedTags = [];
|
Chris@0
|
106
|
Chris@0
|
107 /**
|
Chris@0
|
108 * @var string[][] a map of env var names to their placeholders
|
Chris@0
|
109 */
|
Chris@17
|
110 private $envPlaceholders = [];
|
Chris@0
|
111
|
Chris@0
|
112 /**
|
Chris@0
|
113 * @var int[] a map of env vars to their resolution counter
|
Chris@0
|
114 */
|
Chris@17
|
115 private $envCounters = [];
|
Chris@0
|
116
|
Chris@0
|
117 /**
|
Chris@14
|
118 * @var string[] the list of vendor directories
|
Chris@14
|
119 */
|
Chris@14
|
120 private $vendors;
|
Chris@14
|
121
|
Chris@17
|
122 private $autoconfiguredInstanceof = [];
|
Chris@14
|
123
|
Chris@17
|
124 private $removedIds = [];
|
Chris@14
|
125
|
Chris@18
|
126 private $removedBindingIds = [];
|
Chris@18
|
127
|
Chris@17
|
128 private static $internalTypes = [
|
Chris@16
|
129 'int' => true,
|
Chris@16
|
130 'float' => true,
|
Chris@16
|
131 'string' => true,
|
Chris@16
|
132 'bool' => true,
|
Chris@16
|
133 'resource' => true,
|
Chris@16
|
134 'object' => true,
|
Chris@16
|
135 'array' => true,
|
Chris@16
|
136 'null' => true,
|
Chris@16
|
137 'callable' => true,
|
Chris@16
|
138 'iterable' => true,
|
Chris@16
|
139 'mixed' => true,
|
Chris@17
|
140 ];
|
Chris@16
|
141
|
Chris@14
|
142 public function __construct(ParameterBagInterface $parameterBag = null)
|
Chris@14
|
143 {
|
Chris@14
|
144 parent::__construct($parameterBag);
|
Chris@14
|
145
|
Chris@14
|
146 $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
|
Chris@14
|
147 $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true));
|
Chris@14
|
148 $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false));
|
Chris@14
|
149 $this->setAlias(ContainerInterface::class, new Alias('service_container', false));
|
Chris@14
|
150 }
|
Chris@14
|
151
|
Chris@14
|
152 /**
|
Chris@14
|
153 * @var \ReflectionClass[] a list of class reflectors
|
Chris@14
|
154 */
|
Chris@14
|
155 private $classReflectors;
|
Chris@14
|
156
|
Chris@14
|
157 /**
|
Chris@0
|
158 * Sets the track resources flag.
|
Chris@0
|
159 *
|
Chris@0
|
160 * If you are not using the loaders and therefore don't want
|
Chris@0
|
161 * to depend on the Config component, set this flag to false.
|
Chris@0
|
162 *
|
Chris@14
|
163 * @param bool $track True if you want to track resources, false otherwise
|
Chris@0
|
164 */
|
Chris@0
|
165 public function setResourceTracking($track)
|
Chris@0
|
166 {
|
Chris@0
|
167 $this->trackResources = (bool) $track;
|
Chris@0
|
168 }
|
Chris@0
|
169
|
Chris@0
|
170 /**
|
Chris@0
|
171 * Checks if resources are tracked.
|
Chris@0
|
172 *
|
Chris@14
|
173 * @return bool true If resources are tracked, false otherwise
|
Chris@0
|
174 */
|
Chris@0
|
175 public function isTrackingResources()
|
Chris@0
|
176 {
|
Chris@0
|
177 return $this->trackResources;
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@0
|
180 /**
|
Chris@0
|
181 * Sets the instantiator to be used when fetching proxies.
|
Chris@0
|
182 */
|
Chris@0
|
183 public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
|
Chris@0
|
184 {
|
Chris@0
|
185 $this->proxyInstantiator = $proxyInstantiator;
|
Chris@0
|
186 }
|
Chris@0
|
187
|
Chris@0
|
188 public function registerExtension(ExtensionInterface $extension)
|
Chris@0
|
189 {
|
Chris@0
|
190 $this->extensions[$extension->getAlias()] = $extension;
|
Chris@0
|
191
|
Chris@0
|
192 if (false !== $extension->getNamespace()) {
|
Chris@0
|
193 $this->extensionsByNs[$extension->getNamespace()] = $extension;
|
Chris@0
|
194 }
|
Chris@0
|
195 }
|
Chris@0
|
196
|
Chris@0
|
197 /**
|
Chris@0
|
198 * Returns an extension by alias or namespace.
|
Chris@0
|
199 *
|
Chris@0
|
200 * @param string $name An alias or a namespace
|
Chris@0
|
201 *
|
Chris@0
|
202 * @return ExtensionInterface An extension instance
|
Chris@0
|
203 *
|
Chris@0
|
204 * @throws LogicException if the extension is not registered
|
Chris@0
|
205 */
|
Chris@0
|
206 public function getExtension($name)
|
Chris@0
|
207 {
|
Chris@0
|
208 if (isset($this->extensions[$name])) {
|
Chris@0
|
209 return $this->extensions[$name];
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@0
|
212 if (isset($this->extensionsByNs[$name])) {
|
Chris@0
|
213 return $this->extensionsByNs[$name];
|
Chris@0
|
214 }
|
Chris@0
|
215
|
Chris@0
|
216 throw new LogicException(sprintf('Container extension "%s" is not registered', $name));
|
Chris@0
|
217 }
|
Chris@0
|
218
|
Chris@0
|
219 /**
|
Chris@0
|
220 * Returns all registered extensions.
|
Chris@0
|
221 *
|
Chris@0
|
222 * @return ExtensionInterface[] An array of ExtensionInterface
|
Chris@0
|
223 */
|
Chris@0
|
224 public function getExtensions()
|
Chris@0
|
225 {
|
Chris@0
|
226 return $this->extensions;
|
Chris@0
|
227 }
|
Chris@0
|
228
|
Chris@0
|
229 /**
|
Chris@0
|
230 * Checks if we have an extension.
|
Chris@0
|
231 *
|
Chris@0
|
232 * @param string $name The name of the extension
|
Chris@0
|
233 *
|
Chris@0
|
234 * @return bool If the extension exists
|
Chris@0
|
235 */
|
Chris@0
|
236 public function hasExtension($name)
|
Chris@0
|
237 {
|
Chris@0
|
238 return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
|
Chris@0
|
239 }
|
Chris@0
|
240
|
Chris@0
|
241 /**
|
Chris@0
|
242 * Returns an array of resources loaded to build this configuration.
|
Chris@0
|
243 *
|
Chris@0
|
244 * @return ResourceInterface[] An array of resources
|
Chris@0
|
245 */
|
Chris@0
|
246 public function getResources()
|
Chris@0
|
247 {
|
Chris@14
|
248 return array_values($this->resources);
|
Chris@0
|
249 }
|
Chris@0
|
250
|
Chris@0
|
251 /**
|
Chris@0
|
252 * @return $this
|
Chris@0
|
253 */
|
Chris@0
|
254 public function addResource(ResourceInterface $resource)
|
Chris@0
|
255 {
|
Chris@0
|
256 if (!$this->trackResources) {
|
Chris@0
|
257 return $this;
|
Chris@0
|
258 }
|
Chris@0
|
259
|
Chris@14
|
260 if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
|
Chris@14
|
261 return $this;
|
Chris@14
|
262 }
|
Chris@14
|
263
|
Chris@14
|
264 $this->resources[(string) $resource] = $resource;
|
Chris@0
|
265
|
Chris@0
|
266 return $this;
|
Chris@0
|
267 }
|
Chris@0
|
268
|
Chris@0
|
269 /**
|
Chris@0
|
270 * Sets the resources for this configuration.
|
Chris@0
|
271 *
|
Chris@0
|
272 * @param ResourceInterface[] $resources An array of resources
|
Chris@0
|
273 *
|
Chris@0
|
274 * @return $this
|
Chris@0
|
275 */
|
Chris@0
|
276 public function setResources(array $resources)
|
Chris@0
|
277 {
|
Chris@0
|
278 if (!$this->trackResources) {
|
Chris@0
|
279 return $this;
|
Chris@0
|
280 }
|
Chris@0
|
281
|
Chris@0
|
282 $this->resources = $resources;
|
Chris@0
|
283
|
Chris@0
|
284 return $this;
|
Chris@0
|
285 }
|
Chris@0
|
286
|
Chris@0
|
287 /**
|
Chris@0
|
288 * Adds the object class hierarchy as resources.
|
Chris@0
|
289 *
|
Chris@14
|
290 * @param object|string $object An object instance or class name
|
Chris@0
|
291 *
|
Chris@0
|
292 * @return $this
|
Chris@0
|
293 */
|
Chris@0
|
294 public function addObjectResource($object)
|
Chris@0
|
295 {
|
Chris@0
|
296 if ($this->trackResources) {
|
Chris@17
|
297 if (\is_object($object)) {
|
Chris@17
|
298 $object = \get_class($object);
|
Chris@14
|
299 }
|
Chris@14
|
300 if (!isset($this->classReflectors[$object])) {
|
Chris@14
|
301 $this->classReflectors[$object] = new \ReflectionClass($object);
|
Chris@14
|
302 }
|
Chris@14
|
303 $class = $this->classReflectors[$object];
|
Chris@14
|
304
|
Chris@14
|
305 foreach ($class->getInterfaceNames() as $name) {
|
Chris@14
|
306 if (null === $interface = &$this->classReflectors[$name]) {
|
Chris@14
|
307 $interface = new \ReflectionClass($name);
|
Chris@14
|
308 }
|
Chris@14
|
309 $file = $interface->getFileName();
|
Chris@14
|
310 if (false !== $file && file_exists($file)) {
|
Chris@14
|
311 $this->fileExists($file);
|
Chris@14
|
312 }
|
Chris@14
|
313 }
|
Chris@14
|
314 do {
|
Chris@14
|
315 $file = $class->getFileName();
|
Chris@14
|
316 if (false !== $file && file_exists($file)) {
|
Chris@14
|
317 $this->fileExists($file);
|
Chris@14
|
318 }
|
Chris@14
|
319 foreach ($class->getTraitNames() as $name) {
|
Chris@14
|
320 $this->addObjectResource($name);
|
Chris@14
|
321 }
|
Chris@14
|
322 } while ($class = $class->getParentClass());
|
Chris@0
|
323 }
|
Chris@0
|
324
|
Chris@0
|
325 return $this;
|
Chris@0
|
326 }
|
Chris@0
|
327
|
Chris@0
|
328 /**
|
Chris@0
|
329 * Adds the given class hierarchy as resources.
|
Chris@0
|
330 *
|
Chris@14
|
331 * @return $this
|
Chris@0
|
332 *
|
Chris@14
|
333 * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead.
|
Chris@0
|
334 */
|
Chris@0
|
335 public function addClassResource(\ReflectionClass $class)
|
Chris@0
|
336 {
|
Chris@14
|
337 @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);
|
Chris@14
|
338
|
Chris@14
|
339 return $this->addObjectResource($class->name);
|
Chris@14
|
340 }
|
Chris@14
|
341
|
Chris@14
|
342 /**
|
Chris@14
|
343 * Retrieves the requested reflection class and registers it for resource tracking.
|
Chris@14
|
344 *
|
Chris@14
|
345 * @param string $class
|
Chris@14
|
346 * @param bool $throw
|
Chris@14
|
347 *
|
Chris@14
|
348 * @return \ReflectionClass|null
|
Chris@14
|
349 *
|
Chris@14
|
350 * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
|
Chris@14
|
351 *
|
Chris@14
|
352 * @final
|
Chris@14
|
353 */
|
Chris@14
|
354 public function getReflectionClass($class, $throw = true)
|
Chris@14
|
355 {
|
Chris@14
|
356 if (!$class = $this->getParameterBag()->resolveValue($class)) {
|
Chris@14
|
357 return;
|
Chris@14
|
358 }
|
Chris@16
|
359
|
Chris@16
|
360 if (isset(self::$internalTypes[$class])) {
|
Chris@16
|
361 return null;
|
Chris@16
|
362 }
|
Chris@16
|
363
|
Chris@14
|
364 $resource = null;
|
Chris@14
|
365
|
Chris@14
|
366 try {
|
Chris@14
|
367 if (isset($this->classReflectors[$class])) {
|
Chris@14
|
368 $classReflector = $this->classReflectors[$class];
|
Chris@17
|
369 } elseif (class_exists(ClassExistenceResource::class)) {
|
Chris@14
|
370 $resource = new ClassExistenceResource($class, false);
|
Chris@14
|
371 $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
|
Chris@14
|
372 } else {
|
Chris@17
|
373 $classReflector = class_exists($class) ? new \ReflectionClass($class) : false;
|
Chris@14
|
374 }
|
Chris@14
|
375 } catch (\ReflectionException $e) {
|
Chris@14
|
376 if ($throw) {
|
Chris@14
|
377 throw $e;
|
Chris@14
|
378 }
|
Chris@14
|
379 $classReflector = false;
|
Chris@0
|
380 }
|
Chris@0
|
381
|
Chris@14
|
382 if ($this->trackResources) {
|
Chris@14
|
383 if (!$classReflector) {
|
Chris@14
|
384 $this->addResource($resource ?: new ClassExistenceResource($class, false));
|
Chris@14
|
385 } elseif (!$classReflector->isInternal()) {
|
Chris@14
|
386 $path = $classReflector->getFileName();
|
Chris@14
|
387
|
Chris@14
|
388 if (!$this->inVendors($path)) {
|
Chris@14
|
389 $this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
|
Chris@14
|
390 }
|
Chris@0
|
391 }
|
Chris@14
|
392 $this->classReflectors[$class] = $classReflector;
|
Chris@14
|
393 }
|
Chris@0
|
394
|
Chris@14
|
395 return $classReflector ?: null;
|
Chris@14
|
396 }
|
Chris@14
|
397
|
Chris@14
|
398 /**
|
Chris@14
|
399 * Checks whether the requested file or directory exists and registers the result for resource tracking.
|
Chris@14
|
400 *
|
Chris@14
|
401 * @param string $path The file or directory path for which to check the existence
|
Chris@14
|
402 * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
|
Chris@14
|
403 * it will be used as pattern for tracking contents of the requested directory
|
Chris@14
|
404 *
|
Chris@14
|
405 * @return bool
|
Chris@14
|
406 *
|
Chris@14
|
407 * @final
|
Chris@14
|
408 */
|
Chris@14
|
409 public function fileExists($path, $trackContents = true)
|
Chris@14
|
410 {
|
Chris@14
|
411 $exists = file_exists($path);
|
Chris@14
|
412
|
Chris@14
|
413 if (!$this->trackResources || $this->inVendors($path)) {
|
Chris@14
|
414 return $exists;
|
Chris@14
|
415 }
|
Chris@14
|
416
|
Chris@14
|
417 if (!$exists) {
|
Chris@14
|
418 $this->addResource(new FileExistenceResource($path));
|
Chris@14
|
419
|
Chris@14
|
420 return $exists;
|
Chris@14
|
421 }
|
Chris@14
|
422
|
Chris@14
|
423 if (is_dir($path)) {
|
Chris@14
|
424 if ($trackContents) {
|
Chris@17
|
425 $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null));
|
Chris@14
|
426 } else {
|
Chris@14
|
427 $this->addResource(new GlobResource($path, '/*', false));
|
Chris@14
|
428 }
|
Chris@14
|
429 } elseif ($trackContents) {
|
Chris@14
|
430 $this->addResource(new FileResource($path));
|
Chris@14
|
431 }
|
Chris@14
|
432
|
Chris@14
|
433 return $exists;
|
Chris@0
|
434 }
|
Chris@0
|
435
|
Chris@0
|
436 /**
|
Chris@0
|
437 * Loads the configuration for an extension.
|
Chris@0
|
438 *
|
Chris@0
|
439 * @param string $extension The extension alias or namespace
|
Chris@0
|
440 * @param array $values An array of values that customizes the extension
|
Chris@0
|
441 *
|
Chris@0
|
442 * @return $this
|
Chris@0
|
443 *
|
Chris@14
|
444 * @throws BadMethodCallException When this ContainerBuilder is compiled
|
Chris@14
|
445 * @throws \LogicException if the extension is not registered
|
Chris@0
|
446 */
|
Chris@14
|
447 public function loadFromExtension($extension, array $values = null)
|
Chris@0
|
448 {
|
Chris@14
|
449 if ($this->isCompiled()) {
|
Chris@14
|
450 throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
|
Chris@14
|
451 }
|
Chris@14
|
452
|
Chris@17
|
453 if (\func_num_args() < 2) {
|
Chris@17
|
454 $values = [];
|
Chris@0
|
455 }
|
Chris@0
|
456
|
Chris@0
|
457 $namespace = $this->getExtension($extension)->getAlias();
|
Chris@0
|
458
|
Chris@0
|
459 $this->extensionConfigs[$namespace][] = $values;
|
Chris@0
|
460
|
Chris@0
|
461 return $this;
|
Chris@0
|
462 }
|
Chris@0
|
463
|
Chris@0
|
464 /**
|
Chris@0
|
465 * Adds a compiler pass.
|
Chris@0
|
466 *
|
Chris@0
|
467 * @param CompilerPassInterface $pass A compiler pass
|
Chris@0
|
468 * @param string $type The type of compiler pass
|
Chris@0
|
469 * @param int $priority Used to sort the passes
|
Chris@0
|
470 *
|
Chris@0
|
471 * @return $this
|
Chris@0
|
472 */
|
Chris@14
|
473 public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
|
Chris@0
|
474 {
|
Chris@17
|
475 if (\func_num_args() >= 3) {
|
Chris@0
|
476 $priority = func_get_arg(2);
|
Chris@0
|
477 } else {
|
Chris@17
|
478 if (__CLASS__ !== \get_class($this)) {
|
Chris@0
|
479 $r = new \ReflectionMethod($this, __FUNCTION__);
|
Chris@0
|
480 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
|
Chris@14
|
481 @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);
|
Chris@0
|
482 }
|
Chris@0
|
483 }
|
Chris@0
|
484
|
Chris@0
|
485 $priority = 0;
|
Chris@0
|
486 }
|
Chris@0
|
487
|
Chris@0
|
488 $this->getCompiler()->addPass($pass, $type, $priority);
|
Chris@0
|
489
|
Chris@0
|
490 $this->addObjectResource($pass);
|
Chris@0
|
491
|
Chris@0
|
492 return $this;
|
Chris@0
|
493 }
|
Chris@0
|
494
|
Chris@0
|
495 /**
|
Chris@0
|
496 * Returns the compiler pass config which can then be modified.
|
Chris@0
|
497 *
|
Chris@0
|
498 * @return PassConfig The compiler pass config
|
Chris@0
|
499 */
|
Chris@0
|
500 public function getCompilerPassConfig()
|
Chris@0
|
501 {
|
Chris@0
|
502 return $this->getCompiler()->getPassConfig();
|
Chris@0
|
503 }
|
Chris@0
|
504
|
Chris@0
|
505 /**
|
Chris@0
|
506 * Returns the compiler.
|
Chris@0
|
507 *
|
Chris@0
|
508 * @return Compiler The compiler
|
Chris@0
|
509 */
|
Chris@0
|
510 public function getCompiler()
|
Chris@0
|
511 {
|
Chris@0
|
512 if (null === $this->compiler) {
|
Chris@0
|
513 $this->compiler = new Compiler();
|
Chris@0
|
514 }
|
Chris@0
|
515
|
Chris@0
|
516 return $this->compiler;
|
Chris@0
|
517 }
|
Chris@0
|
518
|
Chris@0
|
519 /**
|
Chris@0
|
520 * Sets a service.
|
Chris@0
|
521 *
|
Chris@0
|
522 * @param string $id The service identifier
|
Chris@0
|
523 * @param object $service The service instance
|
Chris@0
|
524 *
|
Chris@14
|
525 * @throws BadMethodCallException When this ContainerBuilder is compiled
|
Chris@0
|
526 */
|
Chris@0
|
527 public function set($id, $service)
|
Chris@0
|
528 {
|
Chris@14
|
529 $id = $this->normalizeId($id);
|
Chris@0
|
530
|
Chris@14
|
531 if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
|
Chris@14
|
532 // setting a synthetic service on a compiled container is alright
|
Chris@14
|
533 throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
|
Chris@0
|
534 }
|
Chris@0
|
535
|
Chris@14
|
536 unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]);
|
Chris@0
|
537
|
Chris@0
|
538 parent::set($id, $service);
|
Chris@0
|
539 }
|
Chris@0
|
540
|
Chris@0
|
541 /**
|
Chris@0
|
542 * Removes a service definition.
|
Chris@0
|
543 *
|
Chris@0
|
544 * @param string $id The service identifier
|
Chris@0
|
545 */
|
Chris@0
|
546 public function removeDefinition($id)
|
Chris@0
|
547 {
|
Chris@14
|
548 if (isset($this->definitions[$id = $this->normalizeId($id)])) {
|
Chris@14
|
549 unset($this->definitions[$id]);
|
Chris@14
|
550 $this->removedIds[$id] = true;
|
Chris@14
|
551 }
|
Chris@0
|
552 }
|
Chris@0
|
553
|
Chris@0
|
554 /**
|
Chris@0
|
555 * Returns true if the given service is defined.
|
Chris@0
|
556 *
|
Chris@0
|
557 * @param string $id The service identifier
|
Chris@0
|
558 *
|
Chris@0
|
559 * @return bool true if the service is defined, false otherwise
|
Chris@0
|
560 */
|
Chris@0
|
561 public function has($id)
|
Chris@0
|
562 {
|
Chris@14
|
563 $id = $this->normalizeId($id);
|
Chris@0
|
564
|
Chris@0
|
565 return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
|
Chris@0
|
566 }
|
Chris@0
|
567
|
Chris@0
|
568 /**
|
Chris@0
|
569 * Gets a service.
|
Chris@0
|
570 *
|
Chris@0
|
571 * @param string $id The service identifier
|
Chris@0
|
572 * @param int $invalidBehavior The behavior when the service does not exist
|
Chris@0
|
573 *
|
Chris@0
|
574 * @return object The associated service
|
Chris@0
|
575 *
|
Chris@0
|
576 * @throws InvalidArgumentException when no definitions are available
|
Chris@0
|
577 * @throws ServiceCircularReferenceException When a circular reference is detected
|
Chris@0
|
578 * @throws ServiceNotFoundException When the service is not defined
|
Chris@0
|
579 * @throws \Exception
|
Chris@0
|
580 *
|
Chris@0
|
581 * @see Reference
|
Chris@0
|
582 */
|
Chris@0
|
583 public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
|
Chris@0
|
584 {
|
Chris@14
|
585 if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) {
|
Chris@14
|
586 @trigger_error(sprintf('Fetching the "%s" private service or alias is deprecated since Symfony 3.4 and will fail in 4.0. Make it public instead.', $id), E_USER_DEPRECATED);
|
Chris@14
|
587 }
|
Chris@0
|
588
|
Chris@14
|
589 return $this->doGet($id, $invalidBehavior);
|
Chris@14
|
590 }
|
Chris@14
|
591
|
Chris@17
|
592 private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, $isConstructorArgument = false)
|
Chris@14
|
593 {
|
Chris@14
|
594 $id = $this->normalizeId($id);
|
Chris@14
|
595
|
Chris@14
|
596 if (isset($inlineServices[$id])) {
|
Chris@14
|
597 return $inlineServices[$id];
|
Chris@14
|
598 }
|
Chris@17
|
599 if (null === $inlineServices) {
|
Chris@17
|
600 $isConstructorArgument = true;
|
Chris@17
|
601 $inlineServices = [];
|
Chris@14
|
602 }
|
Chris@17
|
603 try {
|
Chris@17
|
604 if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
|
Chris@17
|
605 return parent::get($id, $invalidBehavior);
|
Chris@17
|
606 }
|
Chris@17
|
607 if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
|
Chris@17
|
608 return $service;
|
Chris@17
|
609 }
|
Chris@17
|
610 } catch (ServiceCircularReferenceException $e) {
|
Chris@17
|
611 if ($isConstructorArgument) {
|
Chris@17
|
612 throw $e;
|
Chris@17
|
613 }
|
Chris@0
|
614 }
|
Chris@0
|
615
|
Chris@0
|
616 if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
|
Chris@17
|
617 return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument);
|
Chris@0
|
618 }
|
Chris@0
|
619
|
Chris@0
|
620 try {
|
Chris@0
|
621 $definition = $this->getDefinition($id);
|
Chris@0
|
622 } catch (ServiceNotFoundException $e) {
|
Chris@0
|
623 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
|
Chris@0
|
624 return;
|
Chris@0
|
625 }
|
Chris@0
|
626
|
Chris@0
|
627 throw $e;
|
Chris@0
|
628 }
|
Chris@0
|
629
|
Chris@17
|
630 if ($isConstructorArgument) {
|
Chris@17
|
631 $this->loading[$id] = true;
|
Chris@17
|
632 }
|
Chris@0
|
633
|
Chris@0
|
634 try {
|
Chris@17
|
635 return $this->createService($definition, $inlineServices, $isConstructorArgument, $id);
|
Chris@0
|
636 } finally {
|
Chris@17
|
637 if ($isConstructorArgument) {
|
Chris@17
|
638 unset($this->loading[$id]);
|
Chris@17
|
639 }
|
Chris@0
|
640 }
|
Chris@0
|
641 }
|
Chris@0
|
642
|
Chris@0
|
643 /**
|
Chris@0
|
644 * Merges a ContainerBuilder with the current ContainerBuilder configuration.
|
Chris@0
|
645 *
|
Chris@0
|
646 * Service definitions overrides the current defined ones.
|
Chris@0
|
647 *
|
Chris@0
|
648 * But for parameters, they are overridden by the current ones. It allows
|
Chris@0
|
649 * the parameters passed to the container constructor to have precedence
|
Chris@0
|
650 * over the loaded ones.
|
Chris@0
|
651 *
|
Chris@17
|
652 * $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar']));
|
Chris@17
|
653 * $loader = new LoaderXXX($container);
|
Chris@17
|
654 * $loader->load('resource_name');
|
Chris@17
|
655 * $container->register('foo', 'stdClass');
|
Chris@0
|
656 *
|
Chris@0
|
657 * In the above example, even if the loaded resource defines a foo
|
Chris@0
|
658 * parameter, the value will still be 'bar' as defined in the ContainerBuilder
|
Chris@0
|
659 * constructor.
|
Chris@0
|
660 *
|
Chris@14
|
661 * @throws BadMethodCallException When this ContainerBuilder is compiled
|
Chris@0
|
662 */
|
Chris@14
|
663 public function merge(self $container)
|
Chris@0
|
664 {
|
Chris@14
|
665 if ($this->isCompiled()) {
|
Chris@14
|
666 throw new BadMethodCallException('Cannot merge on a compiled container.');
|
Chris@0
|
667 }
|
Chris@0
|
668
|
Chris@0
|
669 $this->addDefinitions($container->getDefinitions());
|
Chris@0
|
670 $this->addAliases($container->getAliases());
|
Chris@0
|
671 $this->getParameterBag()->add($container->getParameterBag()->all());
|
Chris@0
|
672
|
Chris@0
|
673 if ($this->trackResources) {
|
Chris@0
|
674 foreach ($container->getResources() as $resource) {
|
Chris@0
|
675 $this->addResource($resource);
|
Chris@0
|
676 }
|
Chris@0
|
677 }
|
Chris@0
|
678
|
Chris@0
|
679 foreach ($this->extensions as $name => $extension) {
|
Chris@0
|
680 if (!isset($this->extensionConfigs[$name])) {
|
Chris@17
|
681 $this->extensionConfigs[$name] = [];
|
Chris@0
|
682 }
|
Chris@0
|
683
|
Chris@0
|
684 $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
|
Chris@0
|
685 }
|
Chris@0
|
686
|
Chris@0
|
687 if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
|
Chris@14
|
688 $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
|
Chris@0
|
689 $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
|
Chris@14
|
690 } else {
|
Chris@17
|
691 $envPlaceholders = [];
|
Chris@0
|
692 }
|
Chris@0
|
693
|
Chris@0
|
694 foreach ($container->envCounters as $env => $count) {
|
Chris@14
|
695 if (!$count && !isset($envPlaceholders[$env])) {
|
Chris@14
|
696 continue;
|
Chris@14
|
697 }
|
Chris@0
|
698 if (!isset($this->envCounters[$env])) {
|
Chris@0
|
699 $this->envCounters[$env] = $count;
|
Chris@0
|
700 } else {
|
Chris@0
|
701 $this->envCounters[$env] += $count;
|
Chris@0
|
702 }
|
Chris@0
|
703 }
|
Chris@14
|
704
|
Chris@14
|
705 foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
|
Chris@14
|
706 if (isset($this->autoconfiguredInstanceof[$interface])) {
|
Chris@14
|
707 throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface));
|
Chris@14
|
708 }
|
Chris@14
|
709
|
Chris@14
|
710 $this->autoconfiguredInstanceof[$interface] = $childDefinition;
|
Chris@14
|
711 }
|
Chris@0
|
712 }
|
Chris@0
|
713
|
Chris@0
|
714 /**
|
Chris@0
|
715 * Returns the configuration array for the given extension.
|
Chris@0
|
716 *
|
Chris@0
|
717 * @param string $name The name of the extension
|
Chris@0
|
718 *
|
Chris@0
|
719 * @return array An array of configuration
|
Chris@0
|
720 */
|
Chris@0
|
721 public function getExtensionConfig($name)
|
Chris@0
|
722 {
|
Chris@0
|
723 if (!isset($this->extensionConfigs[$name])) {
|
Chris@17
|
724 $this->extensionConfigs[$name] = [];
|
Chris@0
|
725 }
|
Chris@0
|
726
|
Chris@0
|
727 return $this->extensionConfigs[$name];
|
Chris@0
|
728 }
|
Chris@0
|
729
|
Chris@0
|
730 /**
|
Chris@0
|
731 * Prepends a config array to the configs of the given extension.
|
Chris@0
|
732 *
|
Chris@0
|
733 * @param string $name The name of the extension
|
Chris@0
|
734 * @param array $config The config to set
|
Chris@0
|
735 */
|
Chris@0
|
736 public function prependExtensionConfig($name, array $config)
|
Chris@0
|
737 {
|
Chris@0
|
738 if (!isset($this->extensionConfigs[$name])) {
|
Chris@17
|
739 $this->extensionConfigs[$name] = [];
|
Chris@0
|
740 }
|
Chris@0
|
741
|
Chris@0
|
742 array_unshift($this->extensionConfigs[$name], $config);
|
Chris@0
|
743 }
|
Chris@0
|
744
|
Chris@0
|
745 /**
|
Chris@0
|
746 * Compiles the container.
|
Chris@0
|
747 *
|
Chris@0
|
748 * This method passes the container to compiler
|
Chris@0
|
749 * passes whose job is to manipulate and optimize
|
Chris@0
|
750 * the container.
|
Chris@0
|
751 *
|
Chris@0
|
752 * The main compiler passes roughly do four things:
|
Chris@0
|
753 *
|
Chris@0
|
754 * * The extension configurations are merged;
|
Chris@0
|
755 * * Parameter values are resolved;
|
Chris@0
|
756 * * The parameter bag is frozen;
|
Chris@0
|
757 * * Extension loading is disabled.
|
Chris@14
|
758 *
|
Chris@14
|
759 * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
|
Chris@14
|
760 * env vars or be replaced by uniquely identifiable placeholders.
|
Chris@14
|
761 * Set to "true" when you want to use the current ContainerBuilder
|
Chris@14
|
762 * directly, keep to "false" when the container is dumped instead.
|
Chris@0
|
763 */
|
Chris@14
|
764 public function compile(/*$resolveEnvPlaceholders = false*/)
|
Chris@0
|
765 {
|
Chris@17
|
766 if (1 <= \func_num_args()) {
|
Chris@14
|
767 $resolveEnvPlaceholders = func_get_arg(0);
|
Chris@14
|
768 } else {
|
Chris@14
|
769 if (__CLASS__ !== static::class) {
|
Chris@14
|
770 $r = new \ReflectionMethod($this, __FUNCTION__);
|
Chris@14
|
771 if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) {
|
Chris@14
|
772 @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);
|
Chris@14
|
773 }
|
Chris@14
|
774 }
|
Chris@14
|
775 $resolveEnvPlaceholders = false;
|
Chris@14
|
776 }
|
Chris@0
|
777 $compiler = $this->getCompiler();
|
Chris@0
|
778
|
Chris@0
|
779 if ($this->trackResources) {
|
Chris@0
|
780 foreach ($compiler->getPassConfig()->getPasses() as $pass) {
|
Chris@0
|
781 $this->addObjectResource($pass);
|
Chris@0
|
782 }
|
Chris@0
|
783 }
|
Chris@14
|
784 $bag = $this->getParameterBag();
|
Chris@14
|
785
|
Chris@14
|
786 if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
|
Chris@14
|
787 $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
|
Chris@14
|
788 }
|
Chris@0
|
789
|
Chris@0
|
790 $compiler->compile($this);
|
Chris@0
|
791
|
Chris@0
|
792 foreach ($this->definitions as $id => $definition) {
|
Chris@14
|
793 if ($this->trackResources && $definition->isLazy()) {
|
Chris@14
|
794 $this->getReflectionClass($definition->getClass());
|
Chris@0
|
795 }
|
Chris@0
|
796 }
|
Chris@0
|
797
|
Chris@17
|
798 $this->extensionConfigs = [];
|
Chris@14
|
799
|
Chris@14
|
800 if ($bag instanceof EnvPlaceholderParameterBag) {
|
Chris@14
|
801 if ($resolveEnvPlaceholders) {
|
Chris@14
|
802 $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
|
Chris@14
|
803 }
|
Chris@14
|
804
|
Chris@14
|
805 $this->envPlaceholders = $bag->getEnvPlaceholders();
|
Chris@14
|
806 }
|
Chris@0
|
807
|
Chris@0
|
808 parent::compile();
|
Chris@0
|
809
|
Chris@14
|
810 foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) {
|
Chris@14
|
811 if (!$definition->isPublic() || $definition->isPrivate()) {
|
Chris@14
|
812 $this->removedIds[$id] = true;
|
Chris@14
|
813 }
|
Chris@14
|
814 }
|
Chris@0
|
815 }
|
Chris@0
|
816
|
Chris@0
|
817 /**
|
Chris@0
|
818 * Gets all service ids.
|
Chris@0
|
819 *
|
Chris@0
|
820 * @return array An array of all defined service ids
|
Chris@0
|
821 */
|
Chris@0
|
822 public function getServiceIds()
|
Chris@0
|
823 {
|
Chris@0
|
824 return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()));
|
Chris@0
|
825 }
|
Chris@0
|
826
|
Chris@0
|
827 /**
|
Chris@14
|
828 * Gets removed service or alias ids.
|
Chris@14
|
829 *
|
Chris@14
|
830 * @return array
|
Chris@14
|
831 */
|
Chris@14
|
832 public function getRemovedIds()
|
Chris@14
|
833 {
|
Chris@14
|
834 return $this->removedIds;
|
Chris@14
|
835 }
|
Chris@14
|
836
|
Chris@14
|
837 /**
|
Chris@0
|
838 * Adds the service aliases.
|
Chris@0
|
839 */
|
Chris@0
|
840 public function addAliases(array $aliases)
|
Chris@0
|
841 {
|
Chris@0
|
842 foreach ($aliases as $alias => $id) {
|
Chris@0
|
843 $this->setAlias($alias, $id);
|
Chris@0
|
844 }
|
Chris@0
|
845 }
|
Chris@0
|
846
|
Chris@0
|
847 /**
|
Chris@0
|
848 * Sets the service aliases.
|
Chris@0
|
849 */
|
Chris@0
|
850 public function setAliases(array $aliases)
|
Chris@0
|
851 {
|
Chris@17
|
852 $this->aliasDefinitions = [];
|
Chris@0
|
853 $this->addAliases($aliases);
|
Chris@0
|
854 }
|
Chris@0
|
855
|
Chris@0
|
856 /**
|
Chris@0
|
857 * Sets an alias for an existing service.
|
Chris@0
|
858 *
|
Chris@0
|
859 * @param string $alias The alias to create
|
Chris@0
|
860 * @param string|Alias $id The service to alias
|
Chris@0
|
861 *
|
Chris@14
|
862 * @return Alias
|
Chris@14
|
863 *
|
Chris@0
|
864 * @throws InvalidArgumentException if the id is not a string or an Alias
|
Chris@0
|
865 * @throws InvalidArgumentException if the alias is for itself
|
Chris@0
|
866 */
|
Chris@0
|
867 public function setAlias($alias, $id)
|
Chris@0
|
868 {
|
Chris@14
|
869 $alias = $this->normalizeId($alias);
|
Chris@0
|
870
|
Chris@18
|
871 if ('' === $alias || '\\' === substr($alias, -1) || \strlen($alias) !== strcspn($alias, "\0\r\n'")) {
|
Chris@18
|
872 throw new InvalidArgumentException(sprintf('Invalid alias id: "%s"', $alias));
|
Chris@18
|
873 }
|
Chris@18
|
874
|
Chris@17
|
875 if (\is_string($id)) {
|
Chris@14
|
876 $id = new Alias($this->normalizeId($id));
|
Chris@0
|
877 } elseif (!$id instanceof Alias) {
|
Chris@0
|
878 throw new InvalidArgumentException('$id must be a string, or an Alias object.');
|
Chris@0
|
879 }
|
Chris@0
|
880
|
Chris@0
|
881 if ($alias === (string) $id) {
|
Chris@0
|
882 throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
|
Chris@0
|
883 }
|
Chris@0
|
884
|
Chris@14
|
885 unset($this->definitions[$alias], $this->removedIds[$alias]);
|
Chris@0
|
886
|
Chris@14
|
887 return $this->aliasDefinitions[$alias] = $id;
|
Chris@0
|
888 }
|
Chris@0
|
889
|
Chris@0
|
890 /**
|
Chris@0
|
891 * Removes an alias.
|
Chris@0
|
892 *
|
Chris@0
|
893 * @param string $alias The alias to remove
|
Chris@0
|
894 */
|
Chris@0
|
895 public function removeAlias($alias)
|
Chris@0
|
896 {
|
Chris@14
|
897 if (isset($this->aliasDefinitions[$alias = $this->normalizeId($alias)])) {
|
Chris@14
|
898 unset($this->aliasDefinitions[$alias]);
|
Chris@14
|
899 $this->removedIds[$alias] = true;
|
Chris@14
|
900 }
|
Chris@0
|
901 }
|
Chris@0
|
902
|
Chris@0
|
903 /**
|
Chris@0
|
904 * Returns true if an alias exists under the given identifier.
|
Chris@0
|
905 *
|
Chris@0
|
906 * @param string $id The service identifier
|
Chris@0
|
907 *
|
Chris@0
|
908 * @return bool true if the alias exists, false otherwise
|
Chris@0
|
909 */
|
Chris@0
|
910 public function hasAlias($id)
|
Chris@0
|
911 {
|
Chris@14
|
912 return isset($this->aliasDefinitions[$this->normalizeId($id)]);
|
Chris@0
|
913 }
|
Chris@0
|
914
|
Chris@0
|
915 /**
|
Chris@0
|
916 * Gets all defined aliases.
|
Chris@0
|
917 *
|
Chris@0
|
918 * @return Alias[] An array of aliases
|
Chris@0
|
919 */
|
Chris@0
|
920 public function getAliases()
|
Chris@0
|
921 {
|
Chris@0
|
922 return $this->aliasDefinitions;
|
Chris@0
|
923 }
|
Chris@0
|
924
|
Chris@0
|
925 /**
|
Chris@0
|
926 * Gets an alias.
|
Chris@0
|
927 *
|
Chris@0
|
928 * @param string $id The service identifier
|
Chris@0
|
929 *
|
Chris@0
|
930 * @return Alias An Alias instance
|
Chris@0
|
931 *
|
Chris@0
|
932 * @throws InvalidArgumentException if the alias does not exist
|
Chris@0
|
933 */
|
Chris@0
|
934 public function getAlias($id)
|
Chris@0
|
935 {
|
Chris@14
|
936 $id = $this->normalizeId($id);
|
Chris@0
|
937
|
Chris@0
|
938 if (!isset($this->aliasDefinitions[$id])) {
|
Chris@0
|
939 throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
|
Chris@0
|
940 }
|
Chris@0
|
941
|
Chris@0
|
942 return $this->aliasDefinitions[$id];
|
Chris@0
|
943 }
|
Chris@0
|
944
|
Chris@0
|
945 /**
|
Chris@0
|
946 * Registers a service definition.
|
Chris@0
|
947 *
|
Chris@0
|
948 * This methods allows for simple registration of service definition
|
Chris@0
|
949 * with a fluid interface.
|
Chris@0
|
950 *
|
Chris@14
|
951 * @param string $id The service identifier
|
Chris@14
|
952 * @param string $class|null The service class
|
Chris@0
|
953 *
|
Chris@0
|
954 * @return Definition A Definition instance
|
Chris@0
|
955 */
|
Chris@0
|
956 public function register($id, $class = null)
|
Chris@0
|
957 {
|
Chris@0
|
958 return $this->setDefinition($id, new Definition($class));
|
Chris@0
|
959 }
|
Chris@0
|
960
|
Chris@0
|
961 /**
|
Chris@14
|
962 * Registers an autowired service definition.
|
Chris@14
|
963 *
|
Chris@14
|
964 * This method implements a shortcut for using setDefinition() with
|
Chris@14
|
965 * an autowired definition.
|
Chris@14
|
966 *
|
Chris@14
|
967 * @param string $id The service identifier
|
Chris@17
|
968 * @param string|null $class The service class
|
Chris@14
|
969 *
|
Chris@14
|
970 * @return Definition The created definition
|
Chris@14
|
971 */
|
Chris@14
|
972 public function autowire($id, $class = null)
|
Chris@14
|
973 {
|
Chris@14
|
974 return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
|
Chris@14
|
975 }
|
Chris@14
|
976
|
Chris@14
|
977 /**
|
Chris@0
|
978 * Adds the service definitions.
|
Chris@0
|
979 *
|
Chris@0
|
980 * @param Definition[] $definitions An array of service definitions
|
Chris@0
|
981 */
|
Chris@0
|
982 public function addDefinitions(array $definitions)
|
Chris@0
|
983 {
|
Chris@0
|
984 foreach ($definitions as $id => $definition) {
|
Chris@0
|
985 $this->setDefinition($id, $definition);
|
Chris@0
|
986 }
|
Chris@0
|
987 }
|
Chris@0
|
988
|
Chris@0
|
989 /**
|
Chris@0
|
990 * Sets the service definitions.
|
Chris@0
|
991 *
|
Chris@0
|
992 * @param Definition[] $definitions An array of service definitions
|
Chris@0
|
993 */
|
Chris@0
|
994 public function setDefinitions(array $definitions)
|
Chris@0
|
995 {
|
Chris@17
|
996 $this->definitions = [];
|
Chris@0
|
997 $this->addDefinitions($definitions);
|
Chris@0
|
998 }
|
Chris@0
|
999
|
Chris@0
|
1000 /**
|
Chris@0
|
1001 * Gets all service definitions.
|
Chris@0
|
1002 *
|
Chris@0
|
1003 * @return Definition[] An array of Definition instances
|
Chris@0
|
1004 */
|
Chris@0
|
1005 public function getDefinitions()
|
Chris@0
|
1006 {
|
Chris@0
|
1007 return $this->definitions;
|
Chris@0
|
1008 }
|
Chris@0
|
1009
|
Chris@0
|
1010 /**
|
Chris@0
|
1011 * Sets a service definition.
|
Chris@0
|
1012 *
|
Chris@0
|
1013 * @param string $id The service identifier
|
Chris@0
|
1014 * @param Definition $definition A Definition instance
|
Chris@0
|
1015 *
|
Chris@0
|
1016 * @return Definition the service definition
|
Chris@0
|
1017 *
|
Chris@14
|
1018 * @throws BadMethodCallException When this ContainerBuilder is compiled
|
Chris@0
|
1019 */
|
Chris@0
|
1020 public function setDefinition($id, Definition $definition)
|
Chris@0
|
1021 {
|
Chris@14
|
1022 if ($this->isCompiled()) {
|
Chris@14
|
1023 throw new BadMethodCallException('Adding definition to a compiled container is not allowed');
|
Chris@0
|
1024 }
|
Chris@0
|
1025
|
Chris@14
|
1026 $id = $this->normalizeId($id);
|
Chris@0
|
1027
|
Chris@18
|
1028 if ('' === $id || '\\' === substr($id, -1) || \strlen($id) !== strcspn($id, "\0\r\n'")) {
|
Chris@18
|
1029 throw new InvalidArgumentException(sprintf('Invalid service id: "%s"', $id));
|
Chris@18
|
1030 }
|
Chris@18
|
1031
|
Chris@14
|
1032 unset($this->aliasDefinitions[$id], $this->removedIds[$id]);
|
Chris@0
|
1033
|
Chris@0
|
1034 return $this->definitions[$id] = $definition;
|
Chris@0
|
1035 }
|
Chris@0
|
1036
|
Chris@0
|
1037 /**
|
Chris@0
|
1038 * Returns true if a service definition exists under the given identifier.
|
Chris@0
|
1039 *
|
Chris@0
|
1040 * @param string $id The service identifier
|
Chris@0
|
1041 *
|
Chris@0
|
1042 * @return bool true if the service definition exists, false otherwise
|
Chris@0
|
1043 */
|
Chris@0
|
1044 public function hasDefinition($id)
|
Chris@0
|
1045 {
|
Chris@14
|
1046 return isset($this->definitions[$this->normalizeId($id)]);
|
Chris@0
|
1047 }
|
Chris@0
|
1048
|
Chris@0
|
1049 /**
|
Chris@0
|
1050 * Gets a service definition.
|
Chris@0
|
1051 *
|
Chris@0
|
1052 * @param string $id The service identifier
|
Chris@0
|
1053 *
|
Chris@0
|
1054 * @return Definition A Definition instance
|
Chris@0
|
1055 *
|
Chris@0
|
1056 * @throws ServiceNotFoundException if the service definition does not exist
|
Chris@0
|
1057 */
|
Chris@0
|
1058 public function getDefinition($id)
|
Chris@0
|
1059 {
|
Chris@14
|
1060 $id = $this->normalizeId($id);
|
Chris@0
|
1061
|
Chris@0
|
1062 if (!isset($this->definitions[$id])) {
|
Chris@0
|
1063 throw new ServiceNotFoundException($id);
|
Chris@0
|
1064 }
|
Chris@0
|
1065
|
Chris@0
|
1066 return $this->definitions[$id];
|
Chris@0
|
1067 }
|
Chris@0
|
1068
|
Chris@0
|
1069 /**
|
Chris@0
|
1070 * Gets a service definition by id or alias.
|
Chris@0
|
1071 *
|
Chris@0
|
1072 * The method "unaliases" recursively to return a Definition instance.
|
Chris@0
|
1073 *
|
Chris@0
|
1074 * @param string $id The service identifier or alias
|
Chris@0
|
1075 *
|
Chris@0
|
1076 * @return Definition A Definition instance
|
Chris@0
|
1077 *
|
Chris@0
|
1078 * @throws ServiceNotFoundException if the service definition does not exist
|
Chris@0
|
1079 */
|
Chris@0
|
1080 public function findDefinition($id)
|
Chris@0
|
1081 {
|
Chris@14
|
1082 $id = $this->normalizeId($id);
|
Chris@0
|
1083
|
Chris@17
|
1084 $seen = [];
|
Chris@0
|
1085 while (isset($this->aliasDefinitions[$id])) {
|
Chris@0
|
1086 $id = (string) $this->aliasDefinitions[$id];
|
Chris@14
|
1087
|
Chris@14
|
1088 if (isset($seen[$id])) {
|
Chris@14
|
1089 $seen = array_values($seen);
|
Chris@17
|
1090 $seen = \array_slice($seen, array_search($id, $seen));
|
Chris@14
|
1091 $seen[] = $id;
|
Chris@14
|
1092
|
Chris@14
|
1093 throw new ServiceCircularReferenceException($id, $seen);
|
Chris@14
|
1094 }
|
Chris@14
|
1095
|
Chris@14
|
1096 $seen[$id] = $id;
|
Chris@0
|
1097 }
|
Chris@0
|
1098
|
Chris@0
|
1099 return $this->getDefinition($id);
|
Chris@0
|
1100 }
|
Chris@0
|
1101
|
Chris@0
|
1102 /**
|
Chris@0
|
1103 * Creates a service for a service definition.
|
Chris@0
|
1104 *
|
Chris@0
|
1105 * @param Definition $definition A service definition instance
|
Chris@0
|
1106 * @param string $id The service identifier
|
Chris@0
|
1107 * @param bool $tryProxy Whether to try proxying the service with a lazy proxy
|
Chris@0
|
1108 *
|
Chris@0
|
1109 * @return object The service described by the service definition
|
Chris@0
|
1110 *
|
Chris@0
|
1111 * @throws RuntimeException When the factory definition is incomplete
|
Chris@0
|
1112 * @throws RuntimeException When the service is a synthetic service
|
Chris@0
|
1113 * @throws InvalidArgumentException When configure callable is not callable
|
Chris@0
|
1114 */
|
Chris@17
|
1115 private function createService(Definition $definition, array &$inlineServices, $isConstructorArgument = false, $id = null, $tryProxy = true)
|
Chris@0
|
1116 {
|
Chris@14
|
1117 if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) {
|
Chris@14
|
1118 return $inlineServices[$h];
|
Chris@14
|
1119 }
|
Chris@14
|
1120
|
Chris@14
|
1121 if ($definition instanceof ChildDefinition) {
|
Chris@0
|
1122 throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id));
|
Chris@0
|
1123 }
|
Chris@0
|
1124
|
Chris@0
|
1125 if ($definition->isSynthetic()) {
|
Chris@0
|
1126 throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
|
Chris@0
|
1127 }
|
Chris@0
|
1128
|
Chris@0
|
1129 if ($definition->isDeprecated()) {
|
Chris@0
|
1130 @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED);
|
Chris@0
|
1131 }
|
Chris@0
|
1132
|
Chris@17
|
1133 if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) {
|
Chris@17
|
1134 $proxy = $proxy->instantiateProxy(
|
Chris@17
|
1135 $this,
|
Chris@17
|
1136 $definition,
|
Chris@17
|
1137 $id, function () use ($definition, &$inlineServices, $id) {
|
Chris@17
|
1138 return $this->createService($definition, $inlineServices, true, $id, false);
|
Chris@17
|
1139 }
|
Chris@17
|
1140 );
|
Chris@14
|
1141 $this->shareService($definition, $proxy, $id, $inlineServices);
|
Chris@0
|
1142
|
Chris@0
|
1143 return $proxy;
|
Chris@0
|
1144 }
|
Chris@0
|
1145
|
Chris@0
|
1146 $parameterBag = $this->getParameterBag();
|
Chris@0
|
1147
|
Chris@0
|
1148 if (null !== $definition->getFile()) {
|
Chris@0
|
1149 require_once $parameterBag->resolveValue($definition->getFile());
|
Chris@0
|
1150 }
|
Chris@0
|
1151
|
Chris@17
|
1152 $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices, $isConstructorArgument);
|
Chris@17
|
1153
|
Chris@17
|
1154 if (null !== $factory = $definition->getFactory()) {
|
Chris@17
|
1155 if (\is_array($factory)) {
|
Chris@17
|
1156 $factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]];
|
Chris@17
|
1157 } elseif (!\is_string($factory)) {
|
Chris@17
|
1158 throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
|
Chris@17
|
1159 }
|
Chris@17
|
1160 }
|
Chris@14
|
1161
|
Chris@14
|
1162 if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) {
|
Chris@14
|
1163 return $this->services[$id];
|
Chris@14
|
1164 }
|
Chris@0
|
1165
|
Chris@17
|
1166 if (null !== $factory) {
|
Chris@17
|
1167 $service = \call_user_func_array($factory, $arguments);
|
Chris@0
|
1168
|
Chris@17
|
1169 if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) {
|
Chris@0
|
1170 $r = new \ReflectionClass($factory[0]);
|
Chris@0
|
1171
|
Chris@0
|
1172 if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
|
Chris@0
|
1173 @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);
|
Chris@0
|
1174 }
|
Chris@0
|
1175 }
|
Chris@0
|
1176 } else {
|
Chris@14
|
1177 $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass()));
|
Chris@0
|
1178
|
Chris@0
|
1179 $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
|
Chris@14
|
1180 // don't trigger deprecations for internal uses
|
Chris@14
|
1181 // @deprecated since version 3.3, to be removed in 4.0 along with the deprecated class
|
Chris@17
|
1182 $deprecationWhitelist = ['event_dispatcher' => ContainerAwareEventDispatcher::class];
|
Chris@0
|
1183
|
Chris@14
|
1184 if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationWhitelist[$id]) || $deprecationWhitelist[$id] !== $class)) {
|
Chris@0
|
1185 @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);
|
Chris@0
|
1186 }
|
Chris@0
|
1187 }
|
Chris@0
|
1188
|
Chris@0
|
1189 if ($tryProxy || !$definition->isLazy()) {
|
Chris@0
|
1190 // share only if proxying failed, or if not a proxy
|
Chris@14
|
1191 $this->shareService($definition, $service, $id, $inlineServices);
|
Chris@0
|
1192 }
|
Chris@0
|
1193
|
Chris@14
|
1194 $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices);
|
Chris@0
|
1195 foreach ($properties as $name => $value) {
|
Chris@0
|
1196 $service->$name = $value;
|
Chris@0
|
1197 }
|
Chris@0
|
1198
|
Chris@0
|
1199 foreach ($definition->getMethodCalls() as $call) {
|
Chris@14
|
1200 $this->callMethod($service, $call, $inlineServices);
|
Chris@0
|
1201 }
|
Chris@0
|
1202
|
Chris@0
|
1203 if ($callable = $definition->getConfigurator()) {
|
Chris@17
|
1204 if (\is_array($callable)) {
|
Chris@0
|
1205 $callable[0] = $parameterBag->resolveValue($callable[0]);
|
Chris@0
|
1206
|
Chris@0
|
1207 if ($callable[0] instanceof Reference) {
|
Chris@14
|
1208 $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices);
|
Chris@0
|
1209 } elseif ($callable[0] instanceof Definition) {
|
Chris@14
|
1210 $callable[0] = $this->createService($callable[0], $inlineServices);
|
Chris@0
|
1211 }
|
Chris@0
|
1212 }
|
Chris@0
|
1213
|
Chris@17
|
1214 if (!\is_callable($callable)) {
|
Chris@17
|
1215 throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', \get_class($service)));
|
Chris@0
|
1216 }
|
Chris@0
|
1217
|
Chris@17
|
1218 \call_user_func($callable, $service);
|
Chris@0
|
1219 }
|
Chris@0
|
1220
|
Chris@0
|
1221 return $service;
|
Chris@0
|
1222 }
|
Chris@0
|
1223
|
Chris@0
|
1224 /**
|
Chris@0
|
1225 * Replaces service references by the real service instance and evaluates expressions.
|
Chris@0
|
1226 *
|
Chris@0
|
1227 * @param mixed $value A value
|
Chris@0
|
1228 *
|
Chris@0
|
1229 * @return mixed The same value with all service references replaced by
|
Chris@0
|
1230 * the real service instances and all expressions evaluated
|
Chris@0
|
1231 */
|
Chris@0
|
1232 public function resolveServices($value)
|
Chris@0
|
1233 {
|
Chris@14
|
1234 return $this->doResolveServices($value);
|
Chris@14
|
1235 }
|
Chris@14
|
1236
|
Chris@17
|
1237 private function doResolveServices($value, array &$inlineServices = [], $isConstructorArgument = false)
|
Chris@14
|
1238 {
|
Chris@17
|
1239 if (\is_array($value)) {
|
Chris@0
|
1240 foreach ($value as $k => $v) {
|
Chris@17
|
1241 $value[$k] = $this->doResolveServices($v, $inlineServices, $isConstructorArgument);
|
Chris@0
|
1242 }
|
Chris@14
|
1243 } elseif ($value instanceof ServiceClosureArgument) {
|
Chris@14
|
1244 $reference = $value->getValues()[0];
|
Chris@14
|
1245 $value = function () use ($reference) {
|
Chris@14
|
1246 return $this->resolveServices($reference);
|
Chris@14
|
1247 };
|
Chris@14
|
1248 } elseif ($value instanceof IteratorArgument) {
|
Chris@14
|
1249 $value = new RewindableGenerator(function () use ($value) {
|
Chris@14
|
1250 foreach ($value->getValues() as $k => $v) {
|
Chris@14
|
1251 foreach (self::getServiceConditionals($v) as $s) {
|
Chris@14
|
1252 if (!$this->has($s)) {
|
Chris@14
|
1253 continue 2;
|
Chris@14
|
1254 }
|
Chris@14
|
1255 }
|
Chris@14
|
1256 foreach (self::getInitializedConditionals($v) as $s) {
|
Chris@14
|
1257 if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
|
Chris@14
|
1258 continue 2;
|
Chris@14
|
1259 }
|
Chris@14
|
1260 }
|
Chris@14
|
1261
|
Chris@14
|
1262 yield $k => $this->resolveServices($v);
|
Chris@14
|
1263 }
|
Chris@14
|
1264 }, function () use ($value) {
|
Chris@14
|
1265 $count = 0;
|
Chris@14
|
1266 foreach ($value->getValues() as $v) {
|
Chris@14
|
1267 foreach (self::getServiceConditionals($v) as $s) {
|
Chris@14
|
1268 if (!$this->has($s)) {
|
Chris@14
|
1269 continue 2;
|
Chris@14
|
1270 }
|
Chris@14
|
1271 }
|
Chris@14
|
1272 foreach (self::getInitializedConditionals($v) as $s) {
|
Chris@14
|
1273 if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
|
Chris@14
|
1274 continue 2;
|
Chris@14
|
1275 }
|
Chris@14
|
1276 }
|
Chris@14
|
1277
|
Chris@14
|
1278 ++$count;
|
Chris@14
|
1279 }
|
Chris@14
|
1280
|
Chris@14
|
1281 return $count;
|
Chris@14
|
1282 });
|
Chris@0
|
1283 } elseif ($value instanceof Reference) {
|
Chris@17
|
1284 $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument);
|
Chris@0
|
1285 } elseif ($value instanceof Definition) {
|
Chris@17
|
1286 $value = $this->createService($value, $inlineServices, $isConstructorArgument);
|
Chris@14
|
1287 } elseif ($value instanceof Parameter) {
|
Chris@14
|
1288 $value = $this->getParameter((string) $value);
|
Chris@0
|
1289 } elseif ($value instanceof Expression) {
|
Chris@17
|
1290 $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]);
|
Chris@0
|
1291 }
|
Chris@0
|
1292
|
Chris@0
|
1293 return $value;
|
Chris@0
|
1294 }
|
Chris@0
|
1295
|
Chris@0
|
1296 /**
|
Chris@0
|
1297 * Returns service ids for a given tag.
|
Chris@0
|
1298 *
|
Chris@0
|
1299 * Example:
|
Chris@0
|
1300 *
|
Chris@17
|
1301 * $container->register('foo')->addTag('my.tag', ['hello' => 'world']);
|
Chris@0
|
1302 *
|
Chris@17
|
1303 * $serviceIds = $container->findTaggedServiceIds('my.tag');
|
Chris@17
|
1304 * foreach ($serviceIds as $serviceId => $tags) {
|
Chris@17
|
1305 * foreach ($tags as $tag) {
|
Chris@17
|
1306 * echo $tag['hello'];
|
Chris@17
|
1307 * }
|
Chris@0
|
1308 * }
|
Chris@0
|
1309 *
|
Chris@14
|
1310 * @param string $name
|
Chris@14
|
1311 * @param bool $throwOnAbstract
|
Chris@0
|
1312 *
|
Chris@0
|
1313 * @return array An array of tags with the tagged service as key, holding a list of attribute arrays
|
Chris@0
|
1314 */
|
Chris@14
|
1315 public function findTaggedServiceIds($name, $throwOnAbstract = false)
|
Chris@0
|
1316 {
|
Chris@0
|
1317 $this->usedTags[] = $name;
|
Chris@17
|
1318 $tags = [];
|
Chris@0
|
1319 foreach ($this->getDefinitions() as $id => $definition) {
|
Chris@0
|
1320 if ($definition->hasTag($name)) {
|
Chris@14
|
1321 if ($throwOnAbstract && $definition->isAbstract()) {
|
Chris@14
|
1322 throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
|
Chris@14
|
1323 }
|
Chris@0
|
1324 $tags[$id] = $definition->getTag($name);
|
Chris@0
|
1325 }
|
Chris@0
|
1326 }
|
Chris@0
|
1327
|
Chris@0
|
1328 return $tags;
|
Chris@0
|
1329 }
|
Chris@0
|
1330
|
Chris@0
|
1331 /**
|
Chris@0
|
1332 * Returns all tags the defined services use.
|
Chris@0
|
1333 *
|
Chris@0
|
1334 * @return array An array of tags
|
Chris@0
|
1335 */
|
Chris@0
|
1336 public function findTags()
|
Chris@0
|
1337 {
|
Chris@17
|
1338 $tags = [];
|
Chris@0
|
1339 foreach ($this->getDefinitions() as $id => $definition) {
|
Chris@0
|
1340 $tags = array_merge(array_keys($definition->getTags()), $tags);
|
Chris@0
|
1341 }
|
Chris@0
|
1342
|
Chris@0
|
1343 return array_unique($tags);
|
Chris@0
|
1344 }
|
Chris@0
|
1345
|
Chris@0
|
1346 /**
|
Chris@0
|
1347 * Returns all tags not queried by findTaggedServiceIds.
|
Chris@0
|
1348 *
|
Chris@0
|
1349 * @return string[] An array of tags
|
Chris@0
|
1350 */
|
Chris@0
|
1351 public function findUnusedTags()
|
Chris@0
|
1352 {
|
Chris@0
|
1353 return array_values(array_diff($this->findTags(), $this->usedTags));
|
Chris@0
|
1354 }
|
Chris@0
|
1355
|
Chris@0
|
1356 public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
Chris@0
|
1357 {
|
Chris@0
|
1358 $this->expressionLanguageProviders[] = $provider;
|
Chris@0
|
1359 }
|
Chris@0
|
1360
|
Chris@0
|
1361 /**
|
Chris@0
|
1362 * @return ExpressionFunctionProviderInterface[]
|
Chris@0
|
1363 */
|
Chris@0
|
1364 public function getExpressionLanguageProviders()
|
Chris@0
|
1365 {
|
Chris@0
|
1366 return $this->expressionLanguageProviders;
|
Chris@0
|
1367 }
|
Chris@0
|
1368
|
Chris@0
|
1369 /**
|
Chris@14
|
1370 * Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
|
Chris@14
|
1371 *
|
Chris@14
|
1372 * @param string $interface The class or interface to match
|
Chris@14
|
1373 *
|
Chris@14
|
1374 * @return ChildDefinition
|
Chris@14
|
1375 */
|
Chris@14
|
1376 public function registerForAutoconfiguration($interface)
|
Chris@14
|
1377 {
|
Chris@14
|
1378 if (!isset($this->autoconfiguredInstanceof[$interface])) {
|
Chris@14
|
1379 $this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
|
Chris@14
|
1380 }
|
Chris@14
|
1381
|
Chris@14
|
1382 return $this->autoconfiguredInstanceof[$interface];
|
Chris@14
|
1383 }
|
Chris@14
|
1384
|
Chris@14
|
1385 /**
|
Chris@14
|
1386 * Returns an array of ChildDefinition[] keyed by interface.
|
Chris@14
|
1387 *
|
Chris@14
|
1388 * @return ChildDefinition[]
|
Chris@14
|
1389 */
|
Chris@14
|
1390 public function getAutoconfiguredInstanceof()
|
Chris@14
|
1391 {
|
Chris@14
|
1392 return $this->autoconfiguredInstanceof;
|
Chris@14
|
1393 }
|
Chris@14
|
1394
|
Chris@14
|
1395 /**
|
Chris@0
|
1396 * Resolves env parameter placeholders in a string or an array.
|
Chris@0
|
1397 *
|
Chris@14
|
1398 * @param mixed $value The value to resolve
|
Chris@14
|
1399 * @param string|true|null $format A sprintf() format returning the replacement for each env var name or
|
Chris@14
|
1400 * null to resolve back to the original "%env(VAR)%" format or
|
Chris@14
|
1401 * true to resolve to the actual values of the referenced env vars
|
Chris@14
|
1402 * @param array &$usedEnvs Env vars found while resolving are added to this array
|
Chris@0
|
1403 *
|
Chris@14
|
1404 * @return mixed The value with env parameters resolved if a string or an array is passed
|
Chris@0
|
1405 */
|
Chris@0
|
1406 public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
|
Chris@0
|
1407 {
|
Chris@0
|
1408 if (null === $format) {
|
Chris@0
|
1409 $format = '%%env(%s)%%';
|
Chris@0
|
1410 }
|
Chris@0
|
1411
|
Chris@14
|
1412 $bag = $this->getParameterBag();
|
Chris@14
|
1413 if (true === $format) {
|
Chris@14
|
1414 $value = $bag->resolveValue($value);
|
Chris@14
|
1415 }
|
Chris@14
|
1416
|
Chris@14
|
1417 if (\is_array($value)) {
|
Chris@17
|
1418 $result = [];
|
Chris@0
|
1419 foreach ($value as $k => $v) {
|
Chris@14
|
1420 $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs);
|
Chris@0
|
1421 }
|
Chris@0
|
1422
|
Chris@0
|
1423 return $result;
|
Chris@0
|
1424 }
|
Chris@0
|
1425
|
Chris@14
|
1426 if (!\is_string($value) || 38 > \strlen($value)) {
|
Chris@0
|
1427 return $value;
|
Chris@0
|
1428 }
|
Chris@0
|
1429 $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
|
Chris@0
|
1430
|
Chris@16
|
1431 $completed = false;
|
Chris@0
|
1432 foreach ($envPlaceholders as $env => $placeholders) {
|
Chris@0
|
1433 foreach ($placeholders as $placeholder) {
|
Chris@0
|
1434 if (false !== stripos($value, $placeholder)) {
|
Chris@14
|
1435 if (true === $format) {
|
Chris@14
|
1436 $resolved = $bag->escapeValue($this->getEnv($env));
|
Chris@14
|
1437 } else {
|
Chris@14
|
1438 $resolved = sprintf($format, $env);
|
Chris@14
|
1439 }
|
Chris@14
|
1440 if ($placeholder === $value) {
|
Chris@14
|
1441 $value = $resolved;
|
Chris@16
|
1442 $completed = true;
|
Chris@14
|
1443 } else {
|
Chris@17
|
1444 if (!\is_string($resolved) && !is_numeric($resolved)) {
|
Chris@17
|
1445 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), $this->resolveEnvPlaceholders($value)));
|
Chris@14
|
1446 }
|
Chris@14
|
1447 $value = str_ireplace($placeholder, $resolved, $value);
|
Chris@14
|
1448 }
|
Chris@0
|
1449 $usedEnvs[$env] = $env;
|
Chris@0
|
1450 $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1;
|
Chris@16
|
1451
|
Chris@16
|
1452 if ($completed) {
|
Chris@16
|
1453 break 2;
|
Chris@16
|
1454 }
|
Chris@0
|
1455 }
|
Chris@0
|
1456 }
|
Chris@0
|
1457 }
|
Chris@0
|
1458
|
Chris@0
|
1459 return $value;
|
Chris@0
|
1460 }
|
Chris@0
|
1461
|
Chris@0
|
1462 /**
|
Chris@0
|
1463 * Get statistics about env usage.
|
Chris@0
|
1464 *
|
Chris@0
|
1465 * @return int[] The number of time each env vars has been resolved
|
Chris@0
|
1466 */
|
Chris@0
|
1467 public function getEnvCounters()
|
Chris@0
|
1468 {
|
Chris@0
|
1469 $bag = $this->getParameterBag();
|
Chris@0
|
1470 $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
|
Chris@0
|
1471
|
Chris@0
|
1472 foreach ($envPlaceholders as $env => $placeholders) {
|
Chris@0
|
1473 if (!isset($this->envCounters[$env])) {
|
Chris@0
|
1474 $this->envCounters[$env] = 0;
|
Chris@0
|
1475 }
|
Chris@0
|
1476 }
|
Chris@0
|
1477
|
Chris@0
|
1478 return $this->envCounters;
|
Chris@0
|
1479 }
|
Chris@0
|
1480
|
Chris@0
|
1481 /**
|
Chris@14
|
1482 * @internal
|
Chris@14
|
1483 */
|
Chris@14
|
1484 public function getNormalizedIds()
|
Chris@14
|
1485 {
|
Chris@17
|
1486 $normalizedIds = [];
|
Chris@14
|
1487
|
Chris@14
|
1488 foreach ($this->normalizedIds as $k => $v) {
|
Chris@14
|
1489 if ($v !== (string) $k) {
|
Chris@14
|
1490 $normalizedIds[$k] = $v;
|
Chris@14
|
1491 }
|
Chris@14
|
1492 }
|
Chris@14
|
1493
|
Chris@14
|
1494 return $normalizedIds;
|
Chris@14
|
1495 }
|
Chris@14
|
1496
|
Chris@14
|
1497 /**
|
Chris@14
|
1498 * @final
|
Chris@14
|
1499 */
|
Chris@14
|
1500 public function log(CompilerPassInterface $pass, $message)
|
Chris@14
|
1501 {
|
Chris@16
|
1502 $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message));
|
Chris@14
|
1503 }
|
Chris@14
|
1504
|
Chris@14
|
1505 /**
|
Chris@14
|
1506 * {@inheritdoc}
|
Chris@14
|
1507 */
|
Chris@14
|
1508 public function normalizeId($id)
|
Chris@14
|
1509 {
|
Chris@14
|
1510 if (!\is_string($id)) {
|
Chris@14
|
1511 $id = (string) $id;
|
Chris@14
|
1512 }
|
Chris@14
|
1513
|
Chris@14
|
1514 return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || isset($this->removedIds[$id]) ? $id : parent::normalizeId($id);
|
Chris@14
|
1515 }
|
Chris@14
|
1516
|
Chris@14
|
1517 /**
|
Chris@18
|
1518 * Gets removed binding ids.
|
Chris@18
|
1519 *
|
Chris@18
|
1520 * @return array
|
Chris@18
|
1521 *
|
Chris@18
|
1522 * @internal
|
Chris@18
|
1523 */
|
Chris@18
|
1524 public function getRemovedBindingIds()
|
Chris@18
|
1525 {
|
Chris@18
|
1526 return $this->removedBindingIds;
|
Chris@18
|
1527 }
|
Chris@18
|
1528
|
Chris@18
|
1529 /**
|
Chris@18
|
1530 * Removes bindings for a service.
|
Chris@18
|
1531 *
|
Chris@18
|
1532 * @param string $id The service identifier
|
Chris@18
|
1533 *
|
Chris@18
|
1534 * @internal
|
Chris@18
|
1535 */
|
Chris@18
|
1536 public function removeBindings($id)
|
Chris@18
|
1537 {
|
Chris@18
|
1538 if ($this->hasDefinition($id)) {
|
Chris@18
|
1539 foreach ($this->getDefinition($id)->getBindings() as $key => $binding) {
|
Chris@18
|
1540 list(, $bindingId) = $binding->getValues();
|
Chris@18
|
1541 $this->removedBindingIds[(int) $bindingId] = true;
|
Chris@18
|
1542 }
|
Chris@18
|
1543 }
|
Chris@18
|
1544 }
|
Chris@18
|
1545
|
Chris@18
|
1546 /**
|
Chris@0
|
1547 * Returns the Service Conditionals.
|
Chris@0
|
1548 *
|
Chris@0
|
1549 * @param mixed $value An array of conditionals to return
|
Chris@0
|
1550 *
|
Chris@0
|
1551 * @return array An array of Service conditionals
|
Chris@14
|
1552 *
|
Chris@14
|
1553 * @internal since version 3.4
|
Chris@0
|
1554 */
|
Chris@0
|
1555 public static function getServiceConditionals($value)
|
Chris@0
|
1556 {
|
Chris@17
|
1557 $services = [];
|
Chris@0
|
1558
|
Chris@17
|
1559 if (\is_array($value)) {
|
Chris@0
|
1560 foreach ($value as $v) {
|
Chris@0
|
1561 $services = array_unique(array_merge($services, self::getServiceConditionals($v)));
|
Chris@0
|
1562 }
|
Chris@14
|
1563 } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
|
Chris@0
|
1564 $services[] = (string) $value;
|
Chris@0
|
1565 }
|
Chris@0
|
1566
|
Chris@0
|
1567 return $services;
|
Chris@0
|
1568 }
|
Chris@0
|
1569
|
Chris@0
|
1570 /**
|
Chris@14
|
1571 * Returns the initialized conditionals.
|
Chris@14
|
1572 *
|
Chris@14
|
1573 * @param mixed $value An array of conditionals to return
|
Chris@14
|
1574 *
|
Chris@14
|
1575 * @return array An array of uninitialized conditionals
|
Chris@14
|
1576 *
|
Chris@14
|
1577 * @internal
|
Chris@14
|
1578 */
|
Chris@14
|
1579 public static function getInitializedConditionals($value)
|
Chris@14
|
1580 {
|
Chris@17
|
1581 $services = [];
|
Chris@14
|
1582
|
Chris@17
|
1583 if (\is_array($value)) {
|
Chris@14
|
1584 foreach ($value as $v) {
|
Chris@14
|
1585 $services = array_unique(array_merge($services, self::getInitializedConditionals($v)));
|
Chris@14
|
1586 }
|
Chris@14
|
1587 } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) {
|
Chris@14
|
1588 $services[] = (string) $value;
|
Chris@14
|
1589 }
|
Chris@14
|
1590
|
Chris@14
|
1591 return $services;
|
Chris@14
|
1592 }
|
Chris@14
|
1593
|
Chris@14
|
1594 /**
|
Chris@14
|
1595 * Computes a reasonably unique hash of a value.
|
Chris@14
|
1596 *
|
Chris@14
|
1597 * @param mixed $value A serializable value
|
Chris@14
|
1598 *
|
Chris@14
|
1599 * @return string
|
Chris@14
|
1600 */
|
Chris@14
|
1601 public static function hash($value)
|
Chris@14
|
1602 {
|
Chris@14
|
1603 $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7);
|
Chris@14
|
1604
|
Chris@17
|
1605 return str_replace(['/', '+'], ['.', '_'], strtolower($hash));
|
Chris@14
|
1606 }
|
Chris@14
|
1607
|
Chris@14
|
1608 /**
|
Chris@14
|
1609 * {@inheritdoc}
|
Chris@14
|
1610 */
|
Chris@14
|
1611 protected function getEnv($name)
|
Chris@14
|
1612 {
|
Chris@14
|
1613 $value = parent::getEnv($name);
|
Chris@14
|
1614 $bag = $this->getParameterBag();
|
Chris@14
|
1615
|
Chris@17
|
1616 if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) {
|
Chris@14
|
1617 return $value;
|
Chris@14
|
1618 }
|
Chris@14
|
1619
|
Chris@14
|
1620 foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
|
Chris@14
|
1621 if (isset($placeholders[$value])) {
|
Chris@14
|
1622 $bag = new ParameterBag($bag->all());
|
Chris@14
|
1623
|
Chris@14
|
1624 return $bag->unescapeValue($bag->get("env($name)"));
|
Chris@14
|
1625 }
|
Chris@14
|
1626 }
|
Chris@14
|
1627
|
Chris@14
|
1628 $this->resolving["env($name)"] = true;
|
Chris@14
|
1629 try {
|
Chris@14
|
1630 return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true));
|
Chris@14
|
1631 } finally {
|
Chris@14
|
1632 unset($this->resolving["env($name)"]);
|
Chris@14
|
1633 }
|
Chris@14
|
1634 }
|
Chris@14
|
1635
|
Chris@14
|
1636 private function callMethod($service, $call, array &$inlineServices)
|
Chris@0
|
1637 {
|
Chris@14
|
1638 foreach (self::getServiceConditionals($call[1]) as $s) {
|
Chris@0
|
1639 if (!$this->has($s)) {
|
Chris@0
|
1640 return;
|
Chris@0
|
1641 }
|
Chris@0
|
1642 }
|
Chris@14
|
1643 foreach (self::getInitializedConditionals($call[1]) as $s) {
|
Chris@14
|
1644 if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) {
|
Chris@14
|
1645 return;
|
Chris@14
|
1646 }
|
Chris@14
|
1647 }
|
Chris@0
|
1648
|
Chris@17
|
1649 \call_user_func_array([$service, $call[0]], $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices));
|
Chris@0
|
1650 }
|
Chris@0
|
1651
|
Chris@0
|
1652 /**
|
Chris@0
|
1653 * Shares a given service in the container.
|
Chris@0
|
1654 *
|
Chris@0
|
1655 * @param Definition $definition
|
Chris@14
|
1656 * @param object $service
|
Chris@0
|
1657 * @param string|null $id
|
Chris@0
|
1658 */
|
Chris@14
|
1659 private function shareService(Definition $definition, $service, $id, array &$inlineServices)
|
Chris@0
|
1660 {
|
Chris@14
|
1661 $inlineServices[null !== $id ? $id : spl_object_hash($definition)] = $service;
|
Chris@14
|
1662
|
Chris@0
|
1663 if (null !== $id && $definition->isShared()) {
|
Chris@14
|
1664 $this->services[$id] = $service;
|
Chris@17
|
1665 unset($this->loading[$id]);
|
Chris@0
|
1666 }
|
Chris@0
|
1667 }
|
Chris@0
|
1668
|
Chris@0
|
1669 private function getExpressionLanguage()
|
Chris@0
|
1670 {
|
Chris@0
|
1671 if (null === $this->expressionLanguage) {
|
Chris@0
|
1672 if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
Chris@0
|
1673 throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
Chris@0
|
1674 }
|
Chris@0
|
1675 $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
|
Chris@0
|
1676 }
|
Chris@0
|
1677
|
Chris@0
|
1678 return $this->expressionLanguage;
|
Chris@0
|
1679 }
|
Chris@14
|
1680
|
Chris@14
|
1681 private function inVendors($path)
|
Chris@14
|
1682 {
|
Chris@14
|
1683 if (null === $this->vendors) {
|
Chris@14
|
1684 $resource = new ComposerResource();
|
Chris@14
|
1685 $this->vendors = $resource->getVendors();
|
Chris@14
|
1686 $this->addResource($resource);
|
Chris@14
|
1687 }
|
Chris@14
|
1688 $path = realpath($path) ?: $path;
|
Chris@14
|
1689
|
Chris@14
|
1690 foreach ($this->vendors as $vendor) {
|
Chris@17
|
1691 if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
|
Chris@14
|
1692 return true;
|
Chris@14
|
1693 }
|
Chris@14
|
1694 }
|
Chris@14
|
1695
|
Chris@14
|
1696 return false;
|
Chris@14
|
1697 }
|
Chris@0
|
1698 }
|