Skip to content

Commit 3ffc9ab

Browse files
minor #61215 [DependencyInjection] Update ResolveClassPass to check class existence (GaryPEGEOT)
This PR was squashed before being merged into the 7.4 branch. Discussion ---------- [DependencyInjection] Update `ResolveClassPass` to check class existence | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #54095 | License | MIT ~~Currently doesn't check for incorrect factories, but I'm not sure what would be the best approach~~ Commits ------- dbd5c25 [DependencyInjection] Update `ResolveClassPass` to check class existence
2 parents 27fd051 + dbd5c25 commit 3ffc9ab

File tree

10 files changed

+119
-32
lines changed

10 files changed

+119
-32
lines changed

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,44 +33,44 @@ public function testProcess()
3333
$container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);
3434
$container->addCompilerPass(new TestServiceContainerRealRefPass(), PassConfig::TYPE_AFTER_REMOVING);
3535

36-
$container->register('Test\public_service')
36+
$container->register('test.public_service', 'stdClass')
3737
->setPublic(true)
38-
->addArgument(new Reference('Test\private_used_shared_service'))
39-
->addArgument(new Reference('Test\private_used_non_shared_service'))
40-
->addArgument(new Reference('Test\soon_private_service'))
38+
->addArgument(new Reference('test.private_used_shared_service'))
39+
->addArgument(new Reference('test.private_used_non_shared_service'))
40+
->addArgument(new Reference('test.soon_private_service'))
4141
;
4242

43-
$container->register('Test\soon_private_service')
43+
$container->register('test.soon_private_service', 'stdClass')
4444
->setPublic(true)
4545
->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.42'])
4646
;
47-
$container->register('Test\soon_private_service_decorated')
47+
$container->register('test.soon_private_service_decorated', 'stdClass')
4848
->setPublic(true)
4949
->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.42'])
5050
;
51-
$container->register('Test\soon_private_service_decorator')
52-
->setDecoratedService('Test\soon_private_service_decorated')
53-
->setArguments(['Test\soon_private_service_decorator.inner']);
51+
$container->register('test.soon_private_service_decorator', 'stdClass')
52+
->setDecoratedService('test.soon_private_service_decorated')
53+
->setArguments(['test.soon_private_service_decorator.inner']);
5454

55-
$container->register('Test\private_used_shared_service');
56-
$container->register('Test\private_unused_shared_service');
57-
$container->register('Test\private_used_non_shared_service')->setShared(false);
58-
$container->register('Test\private_unused_non_shared_service')->setShared(false);
55+
$container->register('test.private_used_shared_service', 'stdClass');
56+
$container->register('test.private_unused_shared_service', 'stdClass');
57+
$container->register('test.private_used_non_shared_service', 'stdClass')->setShared(false);
58+
$container->register('test.private_unused_non_shared_service', 'stdClass')->setShared(false);
5959

6060
$container->compile();
6161

6262
$expected = [
63-
'Test\private_used_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_shared_service')),
64-
'Test\private_used_non_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_non_shared_service')),
65-
'Test\soon_private_service' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service')),
66-
'Test\soon_private_service_decorator' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service_decorated')),
67-
'Test\soon_private_service_decorated' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service_decorated')),
63+
'test.private_used_shared_service' => new ServiceClosureArgument(new Reference('test.private_used_shared_service')),
64+
'test.private_used_non_shared_service' => new ServiceClosureArgument(new Reference('test.private_used_non_shared_service')),
65+
'test.soon_private_service' => new ServiceClosureArgument(new Reference('.container.private.test.soon_private_service')),
66+
'test.soon_private_service_decorator' => new ServiceClosureArgument(new Reference('.container.private.test.soon_private_service_decorated')),
67+
'test.soon_private_service_decorated' => new ServiceClosureArgument(new Reference('.container.private.test.soon_private_service_decorated')),
6868
];
6969

7070
$privateServices = $container->getDefinition('test.private_services_locator')->getArgument(0);
7171
unset($privateServices[\Symfony\Component\DependencyInjection\ContainerInterface::class], $privateServices[ContainerInterface::class]);
7272

7373
$this->assertEquals($expected, $privateServices);
74-
$this->assertFalse($container->getDefinition('Test\private_used_non_shared_service')->isShared());
74+
$this->assertFalse($container->getDefinition('test.private_used_non_shared_service')->isShared());
7575
}
7676
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@ public function process(ContainerBuilder $container): void
2727
continue;
2828
}
2929
if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) {
30-
if ($definition instanceof ChildDefinition && !class_exists($id)) {
31-
throw new InvalidArgumentException(\sprintf('Service definition "%s" has a parent but no class, and its name looks like an FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id));
30+
if (!class_exists($id) && !interface_exists($id)) {
31+
$error = $definition instanceof ChildDefinition ?
32+
'has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service' :
33+
'name looks like a FQCN but the class does not exist';
34+
35+
throw new InvalidArgumentException("Service definition \"{$id}\" {$error}. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.");
3236
}
37+
3338
$definition->setClass($id);
3439
}
3540
}

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ public function testResolveClassFromId($serviceId)
3535

3636
public static function provideValidClassId()
3737
{
38-
yield ['Acme\UnknownClass'];
3938
yield [CaseSensitiveClass::class];
4039
}
4140

@@ -62,7 +61,7 @@ public static function provideInvalidClassId()
6261
public function testNonFqcnChildDefinition()
6362
{
6463
$container = new ContainerBuilder();
65-
$parent = $container->register('App\Foo', null);
64+
$parent = $container->register('App\Foo.parent', 'App\Foo');
6665
$child = $container->setDefinition('App\Foo.child', new ChildDefinition('App\Foo'));
6766

6867
(new ResolveClassPass())->process($container);
@@ -74,7 +73,7 @@ public function testNonFqcnChildDefinition()
7473
public function testClassFoundChildDefinition()
7574
{
7675
$container = new ContainerBuilder();
77-
$parent = $container->register('App\Foo', null);
76+
$parent = $container->register('foo.parent', 'App\Foo');
7877
$child = $container->setDefinition(self::class, new ChildDefinition('App\Foo'));
7978

8079
(new ResolveClassPass())->process($container);
@@ -86,11 +85,21 @@ public function testClassFoundChildDefinition()
8685
public function testAmbiguousChildDefinition()
8786
{
8887
$this->expectException(InvalidArgumentException::class);
89-
$this->expectExceptionMessage('Service definition "App\Foo\Child" has a parent but no class, and its name looks like an FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.');
88+
$this->expectExceptionMessage('Service definition "App\Foo\Child" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.');
9089
$container = new ContainerBuilder();
91-
$container->register('App\Foo', null);
90+
$container->register('app.foo', 'App\Foo');
9291
$container->setDefinition('App\Foo\Child', new ChildDefinition('App\Foo'));
9392

9493
(new ResolveClassPass())->process($container);
9594
}
95+
96+
public function testInvalidClassNameDefinition()
97+
{
98+
$this->expectException(InvalidArgumentException::class);
99+
$this->expectExceptionMessage('Service definition "Acme\UnknownClass" name looks like a FQCN but the class does not exist. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.');
100+
$container = new ContainerBuilder();
101+
$container->register('Acme\UnknownClass');
102+
103+
(new ResolveClassPass())->process($container);
104+
}
96105
}

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,11 +1546,9 @@ public function testClassFromId()
15461546
{
15471547
$container = new ContainerBuilder();
15481548

1549-
$unknown = $container->register('Acme\UnknownClass');
15501549
$autoloadClass = $container->register(CaseSensitiveClass::class);
15511550
$container->compile();
15521551

1553-
$this->assertSame('Acme\UnknownClass', $unknown->getClass());
15541552
$this->assertEquals(CaseSensitiveClass::class, $autoloadClass->getClass());
15551553
}
15561554

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
require_once __DIR__.'/../Fixtures/includes/classes.php';
7676
require_once __DIR__.'/../Fixtures/includes/foo.php';
7777
require_once __DIR__.'/../Fixtures/includes/foo_lazy.php';
78+
require_once __DIR__.'/../Fixtures/includes/fixture_app_services.php';
7879

7980
class PhpDumperTest extends TestCase
8081
{
@@ -1317,7 +1318,7 @@ public function testInlineSelfRef()
13171318
->setProperty('bar', $bar)
13181319
->addArgument($bar);
13191320

1320-
$container->register('App\Foo')
1321+
$container->register('App\Foo', 'App\Foo')
13211322
->setPublic(true)
13221323
->addArgument($baz);
13231324

src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container33.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
$container = new ContainerBuilder();
88

9-
$container->register(\Foo\Foo::class)->setPublic(true);
10-
$container->register(\Bar\Foo::class)->setPublic(true);
9+
$container->register('Foo\Foo', \Foo\Foo::class)->setPublic(true);
10+
$container->register('Bar\Foo', \Bar\Foo::class)->setPublic(true);
1111

1212
return $container;

src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_inline_requires.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
$container->register(HotPath\C1::class)->addTag('container.hot_path')->setPublic(true);
1313
$container->register(HotPath\C2::class)->addArgument(new Reference(HotPath\C3::class))->setPublic(true);
1414
$container->register(HotPath\C3::class);
15-
$container->register(ParentNotExists::class)->setPublic(true);
15+
$container->register(ParentNotExists::class, ParentNotExists::class)->setPublic(true);
1616

1717
return $container;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace App;
4+
5+
class BarService
6+
{
7+
}
8+
9+
class Db
10+
{
11+
public Schema $schema;
12+
}
13+
14+
class Bus
15+
{
16+
public Handler1 $handler1;
17+
public Handler2 $handler2;
18+
19+
public function __construct(public Db $db)
20+
{
21+
}
22+
}
23+
24+
class Handler1
25+
{
26+
public function __construct(
27+
public Db $db,
28+
public Schema $schema,
29+
public Processor $processor,
30+
) {
31+
}
32+
}
33+
34+
class Handler2
35+
{
36+
public function __construct(
37+
public Db $db,
38+
public Schema $schema,
39+
public Processor $processor,
40+
) {
41+
}
42+
}
43+
44+
class Processor
45+
{
46+
public function __construct(
47+
public Registry $registry,
48+
public Db $db,
49+
) {
50+
}
51+
}
52+
53+
class Registry
54+
{
55+
public array $processor;
56+
}
57+
58+
class Schema
59+
{
60+
public function __construct(public Db $db)
61+
{
62+
}
63+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,13 @@ public function setOtherInstances($otherInstances)
4848
$this->otherInstances = $otherInstances;
4949
}
5050
}
51+
52+
namespace Acme;
53+
54+
class Foo
55+
{
56+
}
57+
58+
class WithShortCutArgs
59+
{
60+
}

src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Tests\Loader;
1313

1414
require_once __DIR__.'/../Fixtures/includes/AcmeExtension.php';
15+
require_once __DIR__.'/../Fixtures/includes/fixture_app_services.php';
1516

1617
use PHPUnit\Framework\TestCase;
1718
use Symfony\Component\Config\Builder\ConfigBuilderGenerator;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy