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