annotate vendor/symfony/dependency-injection/Compiler/AutowirePass.php @ 19:fa3358dc1485 tip

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