8000 Improving deprecation message when hitting the "deprecated type" look… · enumag/symfony@a990d5c · GitHub
[go: up one dir, main page]

Skip to content

Commit a990d5c

Browse files
committed
Improving deprecation message when hitting the "deprecated type" lookup, but an alias is available
1 parent 44f3482 commit a990d5c

File tree

2 files changed

+86
-25
lines changed

2 files changed

+86
-25
lines changed

src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ protected function processValue($value, $isRoot = false)
114114
private function doProcessValue($value, $isRoot = false)
115115
{
116116
if ($value instanceof TypedReference) {
117-
if ($ref = $this->getAutowiredReference($value)) {
117+
if ($ref = $this->getAutowiredReference($value, $value->getRequiringClass() ? sprintf('for "%s" in "%s"', $value->getType(), $value->getRequiringClass()) : '')) {
118118
return $ref;
119119
}
120120
$this->container->log($this, $this->createTypeNotFoundMessage($value, 'it'));
@@ -282,7 +282,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
282282
continue;
283283
}
284284

285-
if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''))) {
285+
if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''), 'for '.sprintf('argument "$%s" of method "%s()"', $parameter->name, $class.'::'.$method))) {
286286
$failureMessage = $this->createTypeNotFoundMessage($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
287287

288288
if ($parameter->isDefaultValueAvailable()) {
@@ -316,7 +316,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
316316
/**
317317
* @return TypedReference|null A reference to the service matching the given type, if any
318318
*/
319-
private function getAutowiredReference(TypedReference $reference)
319+
private function getAutowiredReference(TypedReference $reference, $deprecationMessage)
320320
{
321321
$this->lastFailure = null;
322322
$type = $reference->getType();
@@ -334,7 +334,14 @@ private function getAutowiredReference(TypedReference $reference)
334334
}
335335

336336
if (isset($this->types[$type])) {
337-
@trigger_error(sprintf('Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0. You should %s the "%s" service to "%s" instead.', isset($this->types[$this->types[$type]]) ? 'alias' : 'rename (or alias)', $this->types[$type], $type), E_USER_DEPRECATED);
337+
$message = 'Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0.';
338+
if ($aliasSuggestion = $this->getAliasesSuggestionForType($type = $reference->getType(), $deprecationMessage)) {
339+
$message .= ' '.$aliasSuggestion;
340+
} else {
341+
$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);
342+
}
343+
344+
@trigger_error($message, E_USER_DEPRECATED);
338345

339346
return new TypedReference($this->types[$type], $type);
340347
}
@@ -490,25 +497,9 @@ private function createTypeNotFoundMessage(TypedReference $reference, $label)
490497

491498
private function createTypeAlternatives(TypedReference $reference)
492499
{
493-
$type = $reference->getType();
494-
$aliases = array();
495-
foreach (class_parents($type) + class_implements($type) as $parent) {
496-
if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) {
497-
$aliases[] = $parent;
498-
}
499-
}
500-
501-
if (1 < $len = count($aliases)) {
502-
$message = ' Try changing the type-hint to one of its parents: ';
503-
for ($i = 0, --$len; $i < $len; ++$i) {
504-
$message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
505-
}
506-
$message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
507-
508-
return $message;
509-
}
510-
if ($aliases) {
511-
return sprintf(' Try changing the type-hint to "%s" instead.', $aliases[0]);
500+
// try suggesting available aliases first
501+
if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) {
502+
return ' '.$message;
512503
}
513504

514505
if (isset($this->ambiguousServiceTypes[$type])) {
@@ -548,4 +539,29 @@ private static function getResourceMetadataForMethod(\ReflectionMethod $method)
548539

549540
return $methodArgumentsMetadata;
550541
}
542+
543+
private function getAliasesSuggestionForType($type, $extraContext = null)
544+
{
545+
$aliases = array();
546+
foreach (class_parents($type) + class_implements($type) as $parent) {
547+
if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) {
548+
$aliases[] = $parent;
549+
}
550+
}
551+
552+
$extraContext = $extraContext ? ' '.$extraContext : '';
553+
if (1 < $len = count($aliases)) {
554+
$message = sprintf('Try changing the type-hint%s to one of its parents: ', $extraContext);
555+
for ($i = 0, --$len; $i < $len; ++$i) {
556+
$message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
557+
}
558+
$message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
559+
560+
return $message;
561+
}
562+
563+
if ($aliases) {
564+
return sprintf('Try changing the type-hint%s to "%s" instead.', $extraContext, $aliases[0]);
565+
}
566+
}
551567
}

src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,28 @@ public function testProcessAutowireParent()
7878
$this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0));
7979
}
8080

81+
/**
82+
* @group legacy
83+
* @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" to "Symfony\Component\DependencyInjection\Tests\Compiler\AInterface" instead.
84+
* @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException
85+
* @expectedExceptionMessageInSymfony4 Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service.
86+
*/
87+
public function testProcessLegacyAutowireWithAvailableInterface()
88+
{
89+
$container = new ContainerBuilder();
90+
91+
$container->setAlias(AInterface::class, B::class);
92+
$container->register(B::class);
93+
$cDefinition = $container->register('c', __NAMESPACE__.'\C');
94+
$cDefinition->setAutowired(true);
95+
96+
(new ResolveClassPass())->process($container);
97+
(new AutowirePass())->process($container);
98+
99+
$this->assertCount(1, $container->getDefinition('c')->getArguments());
100+
$this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0));
101+
}
102+
81103
/**
82104
* @group legacy
83105
* @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should alias the "Symfony\Component\DependencyInjection\Tests\Compiler\F" service to "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" instead.
@@ -730,7 +752,7 @@ public function provideNotWireableCalls()
730752

731753
/**
732754
* @group legacy
733-
* @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "i" service to "Symfony\Component\DependencyInjection\Tests\Compiler\I" instead.
755+
* @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead.
734756
* @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
735757
* @expectedExceptionMessageInSymfony4 Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead.
736758
*/
@@ -747,6 +769,25 @@ public function testByIdAlternative()
747769
$pass->process($container);
748770
}
749771

772+
/**
773+
* @group legacy
774+
* @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for "Symfony\Component\DependencyInjection\Tests\Compiler\A" in "Symfony\Component\DependencyInjection\Tests\Compiler\Bar" to "Symfony\Component\DependencyInjection\Tests\Compiler\AInterface" instead.
775+
*/
776+
public function testTypedReferenceDeprecationNotice()
777+
{
778+
$container = new ContainerBuilder();
779+
780+
$container->register('aClass', A::class);
781+
$container->setAlias(AInterface::class, 'aClass');
782+
$container
783+
->register('bar', Bar::class)
784+
->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class)))
785+
;
786+
787+
$pass = new AutowirePass();
788+
$pass->process($container);
789+
}
790+
750791
/**
751792
* @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
752793
* @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead.
@@ -798,7 +839,11 @@ public function __construct(Foo $foo)
798839
}
799840
}
800841

801-
class A
842+
interface AInterface
843+
{
844+
}
845+
846+
class A implements AInterface
802847
{
803848
public static function create(Foo $foo)
804849
{

0 commit comments

Comments
 (0)
0