annotate vendor/symfony/dependency-injection/Compiler/AutowirePass.php @ 16:c2387f117808

Routine composer update
author Chris Cannam
date Tue, 10 Jul 2018 15:07:59 +0100
parents 1fec387a4317
children 129ea1e6d783
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@0 31 private $definedTypes = array();
Chris@0 32 private $types;
Chris@14 33 private $ambiguousServiceTypes;
Chris@0 34 private $autowired = array();
Chris@14 35 private $lastFailure;
Chris@14 36 private $throwOnAutowiringException;
Chris@14 37 private $autowiringExceptions = array();
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@14 66 $this->autowiringExceptions = array();
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@0 72 $this->definedTypes = array();
Chris@0 73 $this->types = null;
Chris@14 74 $this->ambiguousServiceTypes = null;
Chris@0 75 $this->autowired = array();
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@0 92 $metadata = array();
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@14 150 array_unshift($methodCalls, array($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@14 184 $reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method);
Chris@14 185 }
Chris@14 186
Chris@14 187 $arguments = $this->autowireMethod($reflectionMethod, $arguments);
Chris@14 188
Chris@14 189 if ($arguments !== $call[1]) {
Chris@14 190 $methodCalls[$i][1] = $arguments;
Chris@14 191 }
Chris@14 192 }
Chris@14 193
Chris@14 194 return $methodCalls;
Chris@14 195 }
Chris@14 196
Chris@14 197 /**
Chris@14 198 * Autowires the constructor or a method.
Chris@14 199 *
Chris@14 200 * @param \ReflectionFunctionAbstract $reflectionMethod
Chris@14 201 * @param array $arguments
Chris@14 202 *
Chris@14 203 * @return array The autowired arguments
Chris@14 204 *
Chris@14 205 * @throws AutowiringFailedException
Chris@14 206 */
Chris@14 207 private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments)
Chris@14 208 {
Chris@14 209 $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId;
Chris@14 210 $method = $reflectionMethod->name;
Chris@14 211 $parameters = $reflectionMethod->getParameters();
Chris@14 212 if (method_exists('ReflectionMethod', 'isVariadic') && $reflectionMethod->isVariadic()) {
Chris@0 213 array_pop($parameters);
Chris@0 214 }
Chris@0 215
Chris@0 216 foreach ($parameters as $index => $parameter) {
Chris@0 217 if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
Chris@0 218 continue;
Chris@0 219 }
Chris@0 220
Chris@14 221 $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
Chris@0 222
Chris@14 223 if (!$type) {
Chris@14 224 if (isset($arguments[$index])) {
Chris@0 225 continue;
Chris@0 226 }
Chris@0 227
Chris@14 228 // no default value? Then fail
Chris@14 229 if (!$parameter->isDefaultValueAvailable()) {
Chris@14 230 // For core classes, isDefaultValueAvailable() can
Chris@14 231 // be false when isOptional() returns true. If the
Chris@14 232 // argument *is* optional, allow it to be missing
Chris@14 233 if ($parameter->isOptional()) {
Chris@14 234 continue;
Chris@14 235 }
Chris@16 236 $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false);
Chris@16 237 $type = $type ? sprintf('is type-hinted "%s"', $type) : 'has no type-hint';
Chris@16 238
Chris@16 239 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 240 }
Chris@0 241
Chris@14 242 // specifically pass the default value
Chris@14 243 $arguments[$index] = $parameter->getDefaultValue();
Chris@14 244
Chris@14 245 continue;
Chris@14 246 }
Chris@14 247
Chris@14 248 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 249 $failureMessage = $this->createTypeNotFoundMessage($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
Chris@14 250
Chris@14 251 if ($parameter->isDefaultValueAvailable()) {
Chris@14 252 $value = $parameter->getDefaultValue();
Chris@14 253 } elseif (!$parameter->allowsNull()) {
Chris@14 254 throw new AutowiringFailedException($this->currentId, $failureMessage);
Chris@0 255 }
Chris@14 256 $this->container->log($this, $failureMessage);
Chris@0 257 }
Chris@0 258
Chris@0 259 $arguments[$index] = $value;
Chris@0 260 }
Chris@0 261
Chris@0 262 if ($parameters && !isset($arguments[++$index])) {
Chris@0 263 while (0 <= --$index) {
Chris@0 264 $parameter = $parameters[$index];
Chris@0 265 if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) {
Chris@0 266 break;
Chris@0 267 }
Chris@0 268 unset($arguments[$index]);
Chris@0 269 }
Chris@0 270 }
Chris@0 271
Chris@0 272 // it's possible index 1 was set, then index 0, then 2, etc
Chris@0 273 // make sure that we re-order so they're injected as expected
Chris@0 274 ksort($arguments);
Chris@14 275
Chris@14 276 return $arguments;
Chris@14 277 }
Chris@14 278
Chris@14 279 /**
Chris@14 280 * @return TypedReference|null A reference to the service matching the given type, if any
Chris@14 281 */
Chris@14 282 private function getAutowiredReference(TypedReference $reference, $deprecationMessage)
Chris@14 283 {
Chris@14 284 $this->lastFailure = null;
Chris@14 285 $type = $reference->getType();
Chris@14 286
Chris@14 287 if ($type !== $this->container->normalizeId($reference) || ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract())) {
Chris@14 288 return $reference;
Chris@14 289 }
Chris@14 290
Chris@14 291 if (null === $this->types) {
Chris@14 292 $this->populateAvailableTypes($this->strictMode);
Chris@14 293 }
Chris@14 294
Chris@14 295 if (isset($this->definedTypes[$type])) {
Chris@14 296 return new TypedReference($this->types[$type], $type);
Chris@14 297 }
Chris@14 298
Chris@14 299 if (!$this->strictMode && isset($this->types[$type])) {
Chris@14 300 $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 301 if ($aliasSuggestion = $this->getAliasesSuggestionForType($type = $reference->getType(), $deprecationMessage)) {
Chris@14 302 $message .= ' '.$aliasSuggestion;
Chris@14 303 } else {
Chris@14 304 $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 305 }
Chris@14 306
Chris@14 307 @trigger_error($message, E_USER_DEPRECATED);
Chris@14 308
Chris@14 309 return new TypedReference($this->types[$type], $type);
Chris@14 310 }
Chris@14 311
Chris@14 312 if (!$reference->canBeAutoregistered() || isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) {
Chris@14 313 return;
Chris@14 314 }
Chris@14 315
Chris@14 316 if (isset($this->autowired[$type])) {
Chris@14 317 return $this->autowired[$type] ? new TypedReference($this->autowired[$type], $type) : null;
Chris@14 318 }
Chris@14 319
Chris@14 320 if (!$this->strictMode) {
Chris@14 321 return $this->createAutowiredDefinition($type);
Chris@14 322 }
Chris@0 323 }
Chris@0 324
Chris@0 325 /**
Chris@0 326 * Populates the list of available types.
Chris@0 327 */
Chris@14 328 private function populateAvailableTypes($onlyAutowiringTypes = false)
Chris@0 329 {
Chris@0 330 $this->types = array();
Chris@14 331 if (!$onlyAutowiringTypes) {
Chris@14 332 $this->ambiguousServiceTypes = array();
Chris@14 333 }
Chris@0 334
Chris@0 335 foreach ($this->container->getDefinitions() as $id => $definition) {
Chris@14 336 $this->populateAvailableType($id, $definition, $onlyAutowiringTypes);
Chris@0 337 }
Chris@0 338 }
Chris@0 339
Chris@0 340 /**
Chris@0 341 * Populates the list of available types for a given definition.
Chris@0 342 *
Chris@0 343 * @param string $id
Chris@0 344 * @param Definition $definition
Chris@0 345 */
Chris@14 346 private function populateAvailableType($id, Definition $definition, $onlyAutowiringTypes)
Chris@0 347 {
Chris@0 348 // Never use abstract services
Chris@0 349 if ($definition->isAbstract()) {
Chris@0 350 return;
Chris@0 351 }
Chris@0 352
Chris@14 353 foreach ($definition->getAutowiringTypes(false) as $type) {
Chris@0 354 $this->definedTypes[$type] = true;
Chris@0 355 $this->types[$type] = $id;
Chris@0 356 unset($this->ambiguousServiceTypes[$type]);
Chris@0 357 }
Chris@0 358
Chris@14 359 if ($onlyAutowiringTypes) {
Chris@14 360 return;
Chris@14 361 }
Chris@14 362
Chris@14 363 if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) {
Chris@0 364 return;
Chris@0 365 }
Chris@0 366
Chris@0 367 foreach ($reflectionClass->getInterfaces() as $reflectionInterface) {
Chris@0 368 $this->set($reflectionInterface->name, $id);
Chris@0 369 }
Chris@0 370
Chris@0 371 do {
Chris@0 372 $this->set($reflectionClass->name, $id);
Chris@0 373 } while ($reflectionClass = $reflectionClass->getParentClass());
Chris@0 374 }
Chris@0 375
Chris@0 376 /**
Chris@0 377 * Associates a type and a service id if applicable.
Chris@0 378 *
Chris@0 379 * @param string $type
Chris@0 380 * @param string $id
Chris@0 381 */
Chris@0 382 private function set($type, $id)
Chris@0 383 {
Chris@0 384 if (isset($this->definedTypes[$type])) {
Chris@0 385 return;
Chris@0 386 }
Chris@0 387
Chris@0 388 // is this already a type/class that is known to match multiple services?
Chris@0 389 if (isset($this->ambiguousServiceTypes[$type])) {
Chris@0 390 $this->ambiguousServiceTypes[$type][] = $id;
Chris@0 391
Chris@0 392 return;
Chris@0 393 }
Chris@0 394
Chris@0 395 // check to make sure the type doesn't match multiple services
Chris@0 396 if (!isset($this->types[$type]) || $this->types[$type] === $id) {
Chris@0 397 $this->types[$type] = $id;
Chris@0 398
Chris@0 399 return;
Chris@0 400 }
Chris@0 401
Chris@0 402 // keep an array of all services matching this type
Chris@0 403 if (!isset($this->ambiguousServiceTypes[$type])) {
Chris@0 404 $this->ambiguousServiceTypes[$type] = array($this->types[$type]);
Chris@0 405 unset($this->types[$type]);
Chris@0 406 }
Chris@0 407 $this->ambiguousServiceTypes[$type][] = $id;
Chris@0 408 }
Chris@0 409
Chris@0 410 /**
Chris@0 411 * Registers a definition for the type if possible or throws an exception.
Chris@0 412 *
Chris@14 413 * @param string $type
Chris@0 414 *
Chris@14 415 * @return TypedReference|null A reference to the registered definition
Chris@0 416 */
Chris@14 417 private function createAutowiredDefinition($type)
Chris@0 418 {
Chris@14 419 if (!($typeHint = $this->container->getReflectionClass($type, false)) || !$typeHint->isInstantiable()) {
Chris@14 420 return;
Chris@0 421 }
Chris@0 422
Chris@14 423 $currentId = $this->currentId;
Chris@14 424 $this->currentId = $type;
Chris@14 425 $this->autowired[$type] = $argumentId = sprintf('autowired.%s', $type);
Chris@14 426 $argumentDefinition = new Definition($type);
Chris@14 427 $argumentDefinition->setPublic(false);
Chris@14 428 $argumentDefinition->setAutowired(true);
Chris@14 429
Chris@14 430 try {
Chris@14 431 $originalThrowSetting = $this->throwOnAutowiringException;
Chris@14 432 $this->throwOnAutowiringException = true;
Chris@14 433 $this->processValue($argumentDefinition, true);
Chris@14 434 $this->container->setDefinition($argumentId, $argumentDefinition);
Chris@14 435 } catch (AutowiringFailedException $e) {
Chris@14 436 $this->autowired[$type] = false;
Chris@14 437 $this->lastFailure = $e->getMessage();
Chris@14 438 $this->container->log($this, $this->lastFailure);
Chris@14 439
Chris@14 440 return;
Chris@14 441 } finally {
Chris@14 442 $this->throwOnAutowiringException = $originalThrowSetting;
Chris@14 443 $this->currentId = $currentId;
Chris@0 444 }
Chris@0 445
Chris@14 446 @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 447
Chris@14 448 $this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId));
Chris@0 449
Chris@14 450 return new TypedReference($argumentId, $type);
Chris@14 451 }
Chris@14 452
Chris@14 453 private function createTypeNotFoundMessage(TypedReference $reference, $label)
Chris@14 454 {
Chris@14 455 if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) {
Chris@14 456 // either $type does not exist or a parent class does not exist
Chris@14 457 try {
Chris@14 458 $resource = new ClassExistenceResource($type, false);
Chris@14 459 // isFresh() will explode ONLY if a parent class/trait does not exist
Chris@14 460 $resource->isFresh(0);
Chris@14 461 $parentMsg = false;
Chris@14 462 } catch (\ReflectionException $e) {
Chris@14 463 $parentMsg = $e->getMessage();
Chris@14 464 }
Chris@14 465
Chris@14 466 $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found');
Chris@14 467 } else {
Chris@14 468 $alternatives = $this->createTypeAlternatives($reference);
Chris@14 469 $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';
Chris@14 470 $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives);
Chris@14 471
Chris@14 472 if ($r->isInterface() && !$alternatives) {
Chris@14 473 $message .= ' Did you create a class that implements this interface?';
Chris@14 474 }
Chris@0 475 }
Chris@0 476
Chris@14 477 $message = sprintf('Cannot autowire service "%s": %s %s', $this->currentId, $label, $message);
Chris@14 478
Chris@14 479 if (null !== $this->lastFailure) {
Chris@14 480 $message = $this->lastFailure."\n".$message;
Chris@14 481 $this->lastFailure = null;
Chris@14 482 }
Chris@14 483
Chris@14 484 return $message;
Chris@14 485 }
Chris@14 486
Chris@14 487 private function createTypeAlternatives(TypedReference $reference)
Chris@14 488 {
Chris@14 489 // try suggesting available aliases first
Chris@14 490 if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) {
Chris@14 491 return ' '.$message;
Chris@14 492 }
Chris@14 493 if (null === $this->ambiguousServiceTypes) {
Chris@14 494 $this->populateAvailableTypes();
Chris@14 495 }
Chris@14 496
Chris@14 497 if (isset($this->ambiguousServiceTypes[$type])) {
Chris@14 498 $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type]));
Chris@14 499 } elseif (isset($this->types[$type])) {
Chris@14 500 $message = sprintf('the existing "%s" service', $this->types[$type]);
Chris@14 501 } elseif ($reference->getRequiringClass() && !$reference->canBeAutoregistered() && !$this->strictMode) {
Chris@14 502 return ' It cannot be auto-registered because it is from a different root namespace.';
Chris@14 503 } else {
Chris@14 504 return;
Chris@14 505 }
Chris@14 506
Chris@14 507 return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message);
Chris@0 508 }
Chris@0 509
Chris@0 510 /**
Chris@14 511 * @deprecated since version 3.3, to be removed in 4.0.
Chris@0 512 */
Chris@0 513 private static function getResourceMetadataForMethod(\ReflectionMethod $method)
Chris@0 514 {
Chris@0 515 $methodArgumentsMetadata = array();
Chris@0 516 foreach ($method->getParameters() as $parameter) {
Chris@0 517 try {
Chris@0 518 $class = $parameter->getClass();
Chris@0 519 } catch (\ReflectionException $e) {
Chris@0 520 // type-hint is against a non-existent class
Chris@0 521 $class = false;
Chris@0 522 }
Chris@0 523
Chris@0 524 $isVariadic = method_exists($parameter, 'isVariadic') && $parameter->isVariadic();
Chris@0 525 $methodArgumentsMetadata[] = array(
Chris@0 526 'class' => $class,
Chris@0 527 'isOptional' => $parameter->isOptional(),
Chris@0 528 'defaultValue' => ($parameter->isOptional() && !$isVariadic) ? $parameter->getDefaultValue() : null,
Chris@0 529 );
Chris@0 530 }
Chris@0 531
Chris@0 532 return $methodArgumentsMetadata;
Chris@0 533 }
Chris@14 534
Chris@14 535 private function getAliasesSuggestionForType($type, $extraContext = null)
Chris@14 536 {
Chris@14 537 $aliases = array();
Chris@14 538 foreach (class_parents($type) + class_implements($type) as $parent) {
Chris@14 539 if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) {
Chris@14 540 $aliases[] = $parent;
Chris@14 541 }
Chris@14 542 }
Chris@14 543
Chris@14 544 $extraContext = $extraContext ? ' '.$extraContext : '';
Chris@14 545 if (1 < $len = count($aliases)) {
Chris@14 546 $message = sprintf('Try changing the type-hint%s to one of its parents: ', $extraContext);
Chris@14 547 for ($i = 0, --$len; $i < $len; ++$i) {
Chris@14 548 $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
Chris@14 549 }
Chris@14 550 $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
Chris@14 551
Chris@14 552 return $message;
Chris@14 553 }
Chris@14 554
Chris@14 555 if ($aliases) {
Chris@14 556 return sprintf('Try changing the type-hint%s to "%s" instead.', $extraContext, $aliases[0]);
Chris@14 557 }
Chris@14 558 }
Chris@0 559 }