Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/dependency-injection/Compiler/AutowirePass.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | 4c8ae668cc8c |
children | c2387f117808 |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
9 * file that was distributed with this source code. | 9 * file that was distributed with this source code. |
10 */ | 10 */ |
11 | 11 |
12 namespace Symfony\Component\DependencyInjection\Compiler; | 12 namespace Symfony\Component\DependencyInjection\Compiler; |
13 | 13 |
14 use Symfony\Component\Config\Resource\ClassExistenceResource; | |
14 use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; | 15 use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; |
15 use Symfony\Component\DependencyInjection\ContainerBuilder; | 16 use Symfony\Component\DependencyInjection\ContainerBuilder; |
16 use Symfony\Component\DependencyInjection\Definition; | 17 use Symfony\Component\DependencyInjection\Definition; |
18 use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; | |
17 use Symfony\Component\DependencyInjection\Exception\RuntimeException; | 19 use Symfony\Component\DependencyInjection\Exception\RuntimeException; |
18 use Symfony\Component\DependencyInjection\Reference; | 20 use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; |
21 use Symfony\Component\DependencyInjection\TypedReference; | |
19 | 22 |
20 /** | 23 /** |
21 * Guesses constructor arguments of services definitions and try to instantiate services if necessary. | 24 * Inspects existing service definitions and wires the autowired ones using the type hints of their classes. |
22 * | 25 * |
23 * @author Kévin Dunglas <dunglas@gmail.com> | 26 * @author Kévin Dunglas <dunglas@gmail.com> |
27 * @author Nicolas Grekas <p@tchwork.com> | |
24 */ | 28 */ |
25 class AutowirePass implements CompilerPassInterface | 29 class AutowirePass extends AbstractRecursivePass |
26 { | 30 { |
27 private $container; | |
28 private $reflectionClasses = array(); | |
29 private $definedTypes = array(); | 31 private $definedTypes = array(); |
30 private $types; | 32 private $types; |
31 private $ambiguousServiceTypes = array(); | 33 private $ambiguousServiceTypes; |
32 private $autowired = array(); | 34 private $autowired = array(); |
35 private $lastFailure; | |
36 private $throwOnAutowiringException; | |
37 private $autowiringExceptions = array(); | |
38 private $strictMode; | |
39 | |
40 /** | |
41 * @param bool $throwOnAutowireException Errors can be retrieved via Definition::getErrors() | |
42 */ | |
43 public function __construct($throwOnAutowireException = true) | |
44 { | |
45 $this->throwOnAutowiringException = $throwOnAutowireException; | |
46 } | |
47 | |
48 /** | |
49 * @deprecated since version 3.4, to be removed in 4.0. | |
50 * | |
51 * @return AutowiringFailedException[] | |
52 */ | |
53 public function getAutowiringExceptions() | |
54 { | |
55 @trigger_error('Calling AutowirePass::getAutowiringExceptions() is deprecated since Symfony 3.4 and will be removed in 4.0. Use Definition::getErrors() instead.', E_USER_DEPRECATED); | |
56 | |
57 return $this->autowiringExceptions; | |
58 } | |
33 | 59 |
34 /** | 60 /** |
35 * {@inheritdoc} | 61 * {@inheritdoc} |
36 */ | 62 */ |
37 public function process(ContainerBuilder $container) | 63 public function process(ContainerBuilder $container) |
38 { | 64 { |
39 $throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); }; | 65 // clear out any possibly stored exceptions from before |
40 spl_autoload_register($throwingAutoloader); | 66 $this->autowiringExceptions = array(); |
67 $this->strictMode = $container->hasParameter('container.autowiring.strict_mode') && $container->getParameter('container.autowiring.strict_mode'); | |
41 | 68 |
42 try { | 69 try { |
43 $this->container = $container; | 70 parent::process($container); |
44 foreach ($container->getDefinitions() as $id => $definition) { | |
45 if ($definition->isAutowired()) { | |
46 $this->completeDefinition($id, $definition); | |
47 } | |
48 } | |
49 } finally { | 71 } finally { |
50 spl_autoload_unregister($throwingAutoloader); | |
51 | |
52 // Free memory and remove circular reference to container | |
53 $this->reflectionClasses = array(); | |
54 $this->definedTypes = array(); | 72 $this->definedTypes = array(); |
55 $this->types = null; | 73 $this->types = null; |
56 $this->ambiguousServiceTypes = array(); | 74 $this->ambiguousServiceTypes = null; |
57 $this->autowired = array(); | 75 $this->autowired = array(); |
58 } | 76 } |
59 } | 77 } |
60 | 78 |
61 /** | 79 /** |
62 * Creates a resource to help know if this service has changed. | 80 * Creates a resource to help know if this service has changed. |
63 * | 81 * |
64 * @param \ReflectionClass $reflectionClass | 82 * @param \ReflectionClass $reflectionClass |
65 * | 83 * |
66 * @return AutowireServiceResource | 84 * @return AutowireServiceResource |
85 * | |
86 * @deprecated since version 3.3, to be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead. | |
67 */ | 87 */ |
68 public static function createResourceForClass(\ReflectionClass $reflectionClass) | 88 public static function createResourceForClass(\ReflectionClass $reflectionClass) |
69 { | 89 { |
90 @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.', E_USER_DEPRECATED); | |
91 | |
70 $metadata = array(); | 92 $metadata = array(); |
71 | 93 |
72 if ($constructor = $reflectionClass->getConstructor()) { | 94 foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { |
73 $metadata['__construct'] = self::getResourceMetadataForMethod($constructor); | 95 if (!$reflectionMethod->isStatic()) { |
74 } | 96 $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod); |
75 | 97 } |
76 foreach (self::getSetters($reflectionClass) as $reflectionMethod) { | |
77 $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod); | |
78 } | 98 } |
79 | 99 |
80 return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata); | 100 return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata); |
81 } | 101 } |
82 | 102 |
83 /** | 103 /** |
84 * Wires the given definition. | 104 * {@inheritdoc} |
85 * | 105 */ |
86 * @param string $id | 106 protected function processValue($value, $isRoot = false) |
87 * @param Definition $definition | 107 { |
88 * | 108 try { |
89 * @throws RuntimeException | 109 return $this->doProcessValue($value, $isRoot); |
90 */ | 110 } catch (AutowiringFailedException $e) { |
91 private function completeDefinition($id, Definition $definition) | 111 if ($this->throwOnAutowiringException) { |
92 { | 112 throw $e; |
93 if ($definition->getFactory()) { | 113 } |
94 throw new RuntimeException(sprintf('Service "%s" can use either autowiring or a factory, not both.', $id)); | 114 |
95 } | 115 $this->autowiringExceptions[] = $e; |
96 | 116 $this->container->getDefinition($this->currentId)->addError($e->getMessage()); |
97 if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { | 117 |
98 return; | 118 return parent::processValue($value, $isRoot); |
99 } | 119 } |
100 | 120 } |
101 if ($this->container->isTrackingResources()) { | 121 |
102 $this->container->addResource(static::createResourceForClass($reflectionClass)); | 122 private function doProcessValue($value, $isRoot = false) |
103 } | 123 { |
104 | 124 if ($value instanceof TypedReference) { |
105 if (!$constructor = $reflectionClass->getConstructor()) { | 125 if ($ref = $this->getAutowiredReference($value, $value->getRequiringClass() ? sprintf('for "%s" in "%s"', $value->getType(), $value->getRequiringClass()) : '')) { |
106 return; | 126 return $ref; |
107 } | 127 } |
108 $parameters = $constructor->getParameters(); | 128 $this->container->log($this, $this->createTypeNotFoundMessage($value, 'it')); |
109 if (method_exists('ReflectionMethod', 'isVariadic') && $constructor->isVariadic()) { | 129 } |
130 $value = parent::processValue($value, $isRoot); | |
131 | |
132 if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { | |
133 return $value; | |
134 } | |
135 if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { | |
136 $this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass())); | |
137 | |
138 return $value; | |
139 } | |
140 | |
141 $methodCalls = $value->getMethodCalls(); | |
142 | |
143 try { | |
144 $constructor = $this->getConstructor($value, false); | |
145 } catch (RuntimeException $e) { | |
146 throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e); | |
147 } | |
148 | |
149 if ($constructor) { | |
150 array_unshift($methodCalls, array($constructor, $value->getArguments())); | |
151 } | |
152 | |
153 $methodCalls = $this->autowireCalls($reflectionClass, $methodCalls); | |
154 | |
155 if ($constructor) { | |
156 list(, $arguments) = array_shift($methodCalls); | |
157 | |
158 if ($arguments !== $value->getArguments()) { | |
159 $value->setArguments($arguments); | |
160 } | |
161 } | |
162 | |
163 if ($methodCalls !== $value->getMethodCalls()) { | |
164 $value->setMethodCalls($methodCalls); | |
165 } | |
166 | |
167 return $value; | |
168 } | |
169 | |
170 /** | |
171 * @param \ReflectionClass $reflectionClass | |
172 * @param array $methodCalls | |
173 * | |
174 * @return array | |
175 */ | |
176 private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls) | |
177 { | |
178 foreach ($methodCalls as $i => $call) { | |
179 list($method, $arguments) = $call; | |
180 | |
181 if ($method instanceof \ReflectionFunctionAbstract) { | |
182 $reflectionMethod = $method; | |
183 } else { | |
184 $reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method); | |
185 } | |
186 | |
187 $arguments = $this->autowireMethod($reflectionMethod, $arguments); | |
188 | |
189 if ($arguments !== $call[1]) { | |
190 $methodCalls[$i][1] = $arguments; | |
191 } | |
192 } | |
193 | |
194 return $methodCalls; | |
195 } | |
196 | |
197 /** | |
198 * Autowires the constructor or a method. | |
199 * | |
200 * @param \ReflectionFunctionAbstract $reflectionMethod | |
201 * @param array $arguments | |
202 * | |
203 * @return array The autowired arguments | |
204 * | |
205 * @throws AutowiringFailedException | |
206 */ | |
207 private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments) | |
208 { | |
209 $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; | |
210 $method = $reflectionMethod->name; | |
211 $parameters = $reflectionMethod->getParameters(); | |
212 if (method_exists('ReflectionMethod', 'isVariadic') && $reflectionMethod->isVariadic()) { | |
110 array_pop($parameters); | 213 array_pop($parameters); |
111 } | 214 } |
112 | 215 |
113 $arguments = $definition->getArguments(); | |
114 foreach ($parameters as $index => $parameter) { | 216 foreach ($parameters as $index => $parameter) { |
115 if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { | 217 if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { |
116 continue; | 218 continue; |
117 } | 219 } |
118 | 220 |
119 try { | 221 $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true); |
120 if (!$typeHint = $parameter->getClass()) { | 222 |
121 if (isset($arguments[$index])) { | 223 if (!$type) { |
224 if (isset($arguments[$index])) { | |
225 continue; | |
226 } | |
227 | |
228 // no default value? Then fail | |
229 if (!$parameter->isDefaultValueAvailable()) { | |
230 // For core classes, isDefaultValueAvailable() can | |
231 // be false when isOptional() returns true. If the | |
232 // argument *is* optional, allow it to be missing | |
233 if ($parameter->isOptional()) { | |
122 continue; | 234 continue; |
123 } | 235 } |
124 | 236 throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" must have a type-hint or be given a value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); |
125 // no default value? Then fail | |
126 if (!$parameter->isOptional()) { | |
127 throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id)); | |
128 } | |
129 | |
130 // specifically pass the default value | |
131 $arguments[$index] = $parameter->getDefaultValue(); | |
132 | |
133 continue; | |
134 } | 237 } |
135 | 238 |
136 if (isset($this->autowired[$typeHint->name])) { | 239 // specifically pass the default value |
137 $arguments[$index] = $this->autowired[$typeHint->name] ? new Reference($this->autowired[$typeHint->name]) : null; | 240 $arguments[$index] = $parameter->getDefaultValue(); |
138 continue; | 241 |
242 continue; | |
243 } | |
244 | |
245 if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''), 'for '.sprintf('argument "$%s" of method "%s()"', $parameter->name, $class.'::'.$method))) { | |
246 $failureMessage = $this->createTypeNotFoundMessage($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); | |
247 | |
248 if ($parameter->isDefaultValueAvailable()) { | |
249 $value = $parameter->getDefaultValue(); | |
250 } elseif (!$parameter->allowsNull()) { | |
251 throw new AutowiringFailedException($this->currentId, $failureMessage); | |
139 } | 252 } |
140 | 253 $this->container->log($this, $failureMessage); |
141 if (null === $this->types) { | |
142 $this->populateAvailableTypes(); | |
143 } | |
144 | |
145 if (isset($this->types[$typeHint->name])) { | |
146 $value = new Reference($this->types[$typeHint->name]); | |
147 } else { | |
148 try { | |
149 $value = $this->createAutowiredDefinition($typeHint, $id); | |
150 } catch (RuntimeException $e) { | |
151 if ($parameter->isDefaultValueAvailable()) { | |
152 $value = $parameter->getDefaultValue(); | |
153 } elseif ($parameter->allowsNull()) { | |
154 $value = null; | |
155 } else { | |
156 throw $e; | |
157 } | |
158 $this->autowired[$typeHint->name] = false; | |
159 } | |
160 } | |
161 } catch (\ReflectionException $e) { | |
162 // Typehint against a non-existing class | |
163 | |
164 if (!$parameter->isDefaultValueAvailable()) { | |
165 throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e); | |
166 } | |
167 | |
168 $value = $parameter->getDefaultValue(); | |
169 } | 254 } |
170 | 255 |
171 $arguments[$index] = $value; | 256 $arguments[$index] = $value; |
172 } | 257 } |
173 | 258 |
182 } | 267 } |
183 | 268 |
184 // it's possible index 1 was set, then index 0, then 2, etc | 269 // it's possible index 1 was set, then index 0, then 2, etc |
185 // make sure that we re-order so they're injected as expected | 270 // make sure that we re-order so they're injected as expected |
186 ksort($arguments); | 271 ksort($arguments); |
187 $definition->setArguments($arguments); | 272 |
273 return $arguments; | |
274 } | |
275 | |
276 /** | |
277 * @return TypedReference|null A reference to the service matching the given type, if any | |
278 */ | |
279 private function getAutowiredReference(TypedReference $reference, $deprecationMessage) | |
280 { | |
281 $this->lastFailure = null; | |
282 $type = $reference->getType(); | |
283 | |
284 if ($type !== $this->container->normalizeId($reference) || ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract())) { | |
285 return $reference; | |
286 } | |
287 | |
288 if (null === $this->types) { | |
289 $this->populateAvailableTypes($this->strictMode); | |
290 } | |
291 | |
292 if (isset($this->definedTypes[$type])) { | |
293 return new TypedReference($this->types[$type], $type); | |
294 } | |
295 | |
296 if (!$this->strictMode && isset($this->types[$type])) { | |
297 $message = 'Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0.'; | |
298 if ($aliasSuggestion = $this->getAliasesSuggestionForType($type = $reference->getType(), $deprecationMessage)) { | |
299 $message .= ' '.$aliasSuggestion; | |
300 } else { | |
301 $message .= sprintf(' You should %s the "%s" service to "%s" instead.', isset($this->types[$this->types[$type]]) ? 'alias' : 'rename (or alias)', $this->types[$type], $type); | |
302 } | |
303 | |
304 @trigger_error($message, E_USER_DEPRECATED); | |
305 | |
306 return new TypedReference($this->types[$type], $type); | |
307 } | |
308 | |
309 if (!$reference->canBeAutoregistered() || isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) { | |
310 return; | |
311 } | |
312 | |
313 if (isset($this->autowired[$type])) { | |
314 return $this->autowired[$type] ? new TypedReference($this->autowired[$type], $type) : null; | |
315 } | |
316 | |
317 if (!$this->strictMode) { | |
318 return $this->createAutowiredDefinition($type); | |
319 } | |
188 } | 320 } |
189 | 321 |
190 /** | 322 /** |
191 * Populates the list of available types. | 323 * Populates the list of available types. |
192 */ | 324 */ |
193 private function populateAvailableTypes() | 325 private function populateAvailableTypes($onlyAutowiringTypes = false) |
194 { | 326 { |
195 $this->types = array(); | 327 $this->types = array(); |
328 if (!$onlyAutowiringTypes) { | |
329 $this->ambiguousServiceTypes = array(); | |
330 } | |
196 | 331 |
197 foreach ($this->container->getDefinitions() as $id => $definition) { | 332 foreach ($this->container->getDefinitions() as $id => $definition) { |
198 $this->populateAvailableType($id, $definition); | 333 $this->populateAvailableType($id, $definition, $onlyAutowiringTypes); |
199 } | 334 } |
200 } | 335 } |
201 | 336 |
202 /** | 337 /** |
203 * Populates the list of available types for a given definition. | 338 * Populates the list of available types for a given definition. |
204 * | 339 * |
205 * @param string $id | 340 * @param string $id |
206 * @param Definition $definition | 341 * @param Definition $definition |
207 */ | 342 */ |
208 private function populateAvailableType($id, Definition $definition) | 343 private function populateAvailableType($id, Definition $definition, $onlyAutowiringTypes) |
209 { | 344 { |
210 // Never use abstract services | 345 // Never use abstract services |
211 if ($definition->isAbstract()) { | 346 if ($definition->isAbstract()) { |
212 return; | 347 return; |
213 } | 348 } |
214 | 349 |
215 foreach ($definition->getAutowiringTypes() as $type) { | 350 foreach ($definition->getAutowiringTypes(false) as $type) { |
216 $this->definedTypes[$type] = true; | 351 $this->definedTypes[$type] = true; |
217 $this->types[$type] = $id; | 352 $this->types[$type] = $id; |
218 unset($this->ambiguousServiceTypes[$type]); | 353 unset($this->ambiguousServiceTypes[$type]); |
219 } | 354 } |
220 | 355 |
221 if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { | 356 if ($onlyAutowiringTypes) { |
357 return; | |
358 } | |
359 | |
360 if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) { | |
222 return; | 361 return; |
223 } | 362 } |
224 | 363 |
225 foreach ($reflectionClass->getInterfaces() as $reflectionInterface) { | 364 foreach ($reflectionClass->getInterfaces() as $reflectionInterface) { |
226 $this->set($reflectionInterface->name, $id); | 365 $this->set($reflectionInterface->name, $id); |
266 } | 405 } |
267 | 406 |
268 /** | 407 /** |
269 * Registers a definition for the type if possible or throws an exception. | 408 * Registers a definition for the type if possible or throws an exception. |
270 * | 409 * |
271 * @param \ReflectionClass $typeHint | 410 * @param string $type |
272 * @param string $id | 411 * |
273 * | 412 * @return TypedReference|null A reference to the registered definition |
274 * @return Reference A reference to the registered definition | 413 */ |
275 * | 414 private function createAutowiredDefinition($type) |
276 * @throws RuntimeException | 415 { |
277 */ | 416 if (!($typeHint = $this->container->getReflectionClass($type, false)) || !$typeHint->isInstantiable()) { |
278 private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) | 417 return; |
279 { | 418 } |
280 if (isset($this->ambiguousServiceTypes[$typeHint->name])) { | 419 |
281 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; | 420 $currentId = $this->currentId; |
282 $matchingServices = implode(', ', $this->ambiguousServiceTypes[$typeHint->name]); | 421 $this->currentId = $type; |
283 | 422 $this->autowired[$type] = $argumentId = sprintf('autowired.%s', $type); |
284 throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices)); | 423 $argumentDefinition = new Definition($type); |
285 } | |
286 | |
287 if (!$typeHint->isInstantiable()) { | |
288 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; | |
289 throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface)); | |
290 } | |
291 | |
292 $this->autowired[$typeHint->name] = $argumentId = sprintf('autowired.%s', $typeHint->name); | |
293 | |
294 $argumentDefinition = $this->container->register($argumentId, $typeHint->name); | |
295 $argumentDefinition->setPublic(false); | 424 $argumentDefinition->setPublic(false); |
425 $argumentDefinition->setAutowired(true); | |
296 | 426 |
297 try { | 427 try { |
298 $this->completeDefinition($argumentId, $argumentDefinition); | 428 $originalThrowSetting = $this->throwOnAutowiringException; |
299 } catch (RuntimeException $e) { | 429 $this->throwOnAutowiringException = true; |
300 $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; | 430 $this->processValue($argumentDefinition, true); |
301 $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface); | 431 $this->container->setDefinition($argumentId, $argumentDefinition); |
302 throw new RuntimeException($message, 0, $e); | 432 } catch (AutowiringFailedException $e) { |
303 } | 433 $this->autowired[$type] = false; |
304 | 434 $this->lastFailure = $e->getMessage(); |
305 return new Reference($argumentId); | 435 $this->container->log($this, $this->lastFailure); |
306 } | 436 |
307 | 437 return; |
308 /** | 438 } finally { |
309 * Retrieves the reflection class associated with the given service. | 439 $this->throwOnAutowiringException = $originalThrowSetting; |
310 * | 440 $this->currentId = $currentId; |
311 * @param string $id | 441 } |
312 * @param Definition $definition | 442 |
313 * | 443 @trigger_error(sprintf('Relying on service auto-registration for type "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Create a service named "%s" instead.', $type, $type), E_USER_DEPRECATED); |
314 * @return \ReflectionClass|false | 444 |
315 */ | 445 $this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId)); |
316 private function getReflectionClass($id, Definition $definition) | 446 |
317 { | 447 return new TypedReference($argumentId, $type); |
318 if (isset($this->reflectionClasses[$id])) { | 448 } |
319 return $this->reflectionClasses[$id]; | 449 |
320 } | 450 private function createTypeNotFoundMessage(TypedReference $reference, $label) |
321 | 451 { |
322 // Cannot use reflection if the class isn't set | 452 if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) { |
323 if (!$class = $definition->getClass()) { | 453 // either $type does not exist or a parent class does not exist |
324 return false; | 454 try { |
325 } | 455 $resource = new ClassExistenceResource($type, false); |
326 | 456 // isFresh() will explode ONLY if a parent class/trait does not exist |
327 $class = $this->container->getParameterBag()->resolveValue($class); | 457 $resource->isFresh(0); |
328 | 458 $parentMsg = false; |
329 if ($deprecated = $definition->isDeprecated()) { | 459 } catch (\ReflectionException $e) { |
330 $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { | 460 $parentMsg = $e->getMessage(); |
331 return (E_USER_DEPRECATED === $level || !$prevErrorHandler) ? false : $prevErrorHandler($level, $message, $file, $line); | 461 } |
332 }); | 462 |
333 } | 463 $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found'); |
334 | 464 } else { |
335 $e = null; | 465 $alternatives = $this->createTypeAlternatives($reference); |
336 | 466 $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists'; |
337 try { | 467 $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives); |
338 $reflector = new \ReflectionClass($class); | 468 |
339 } catch (\Exception $e) { | 469 if ($r->isInterface() && !$alternatives) { |
340 } catch (\Throwable $e) { | 470 $message .= ' Did you create a class that implements this interface?'; |
341 } | 471 } |
342 | 472 } |
343 if ($deprecated) { | 473 |
344 restore_error_handler(); | 474 $message = sprintf('Cannot autowire service "%s": %s %s', $this->currentId, $label, $message); |
345 } | 475 |
346 | 476 if (null !== $this->lastFailure) { |
347 if (null !== $e) { | 477 $message = $this->lastFailure."\n".$message; |
348 if (!$e instanceof \ReflectionException) { | 478 $this->lastFailure = null; |
349 throw $e; | 479 } |
350 } | 480 |
351 $reflector = false; | 481 return $message; |
352 } | 482 } |
353 | 483 |
354 return $this->reflectionClasses[$id] = $reflector; | 484 private function createTypeAlternatives(TypedReference $reference) |
355 } | 485 { |
356 | 486 // try suggesting available aliases first |
357 /** | 487 if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) { |
358 * @param \ReflectionClass $reflectionClass | 488 return ' '.$message; |
359 * | 489 } |
360 * @return \ReflectionMethod[] | 490 if (null === $this->ambiguousServiceTypes) { |
361 */ | 491 $this->populateAvailableTypes(); |
362 private static function getSetters(\ReflectionClass $reflectionClass) | 492 } |
363 { | 493 |
364 foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { | 494 if (isset($this->ambiguousServiceTypes[$type])) { |
365 if (!$reflectionMethod->isStatic() && 1 === $reflectionMethod->getNumberOfParameters() && 0 === strpos($reflectionMethod->name, 'set')) { | 495 $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); |
366 yield $reflectionMethod; | 496 } elseif (isset($this->types[$type])) { |
367 } | 497 $message = sprintf('the existing "%s" service', $this->types[$type]); |
368 } | 498 } elseif ($reference->getRequiringClass() && !$reference->canBeAutoregistered() && !$this->strictMode) { |
369 } | 499 return ' It cannot be auto-registered because it is from a different root namespace.'; |
370 | 500 } else { |
501 return; | |
502 } | |
503 | |
504 return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message); | |
505 } | |
506 | |
507 /** | |
508 * @deprecated since version 3.3, to be removed in 4.0. | |
509 */ | |
371 private static function getResourceMetadataForMethod(\ReflectionMethod $method) | 510 private static function getResourceMetadataForMethod(\ReflectionMethod $method) |
372 { | 511 { |
373 $methodArgumentsMetadata = array(); | 512 $methodArgumentsMetadata = array(); |
374 foreach ($method->getParameters() as $parameter) { | 513 foreach ($method->getParameters() as $parameter) { |
375 try { | 514 try { |
387 ); | 526 ); |
388 } | 527 } |
389 | 528 |
390 return $methodArgumentsMetadata; | 529 return $methodArgumentsMetadata; |
391 } | 530 } |
531 | |
532 private function getAliasesSuggestionForType($type, $extraContext = null) | |
533 { | |
534 $aliases = array(); | |
535 foreach (class_parents($type) + class_implements($type) as $parent) { | |
536 if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) { | |
537 $aliases[] = $parent; | |
538 } | |
539 } | |
540 | |
541 $extraContext = $extraContext ? ' '.$extraContext : ''; | |
542 if (1 < $len = count($aliases)) { | |
543 $message = sprintf('Try changing the type-hint%s to one of its parents: ', $extraContext); | |
544 for ($i = 0, --$len; $i < $len; ++$i) { | |
545 $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); | |
546 } | |
547 $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); | |
548 | |
549 return $message; | |
550 } | |
551 | |
552 if ($aliases) { | |
553 return sprintf('Try changing the type-hint%s to "%s" instead.', $extraContext, $aliases[0]); | |
554 } | |
555 } | |
392 } | 556 } |