8000 bug #33350 [DI] scope singly-implemented interfaces detection by file… · symfony/symfony@db5cf1a · GitHub
[go: up one dir, main page]

Skip to content

Commit db5cf1a

Browse files
committed
bug #33350 [DI] scope singly-implemented interfaces detection by file (daniel-iwaniec, nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [DI] scope singly-implemented interfaces detection by file | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | BC breaks? | yes | Deprecations? | no | Tests pass? | yes | License | MIT [DependencyInjection] fixed handling singly implemented interfaces when importing multiple resources for example: ```yaml App\Adapter\: resource: '../src/Adapter/*' App\Port\: resource: '../src/Port/*' ``` this configuration wont create service for interface (in other words singly implemented interface wont be autowired) and this chage fixes it **Also** this will prevent false positives - for example if I had one implementation in \App\Port namespace and another in \App\Adapter then interface service would still be registered but that could potentially break exisitng code not aware of this bug Commits ------- c1f3970 [DI] add FileLoader::registerAliasesForSinglyImplementedInterfaces() bec3890 [DI] scope singly-implemented interfaces detection by file
2 parents 4cf7ec1 + c1f3970 commit db5cf1a

26 files changed

+310
-51
lines changed

UPGRADE-4.4.md

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,37 @@ Debug
1616
DependencyInjection
1717
-------------------
1818

19+
* Made singly-implemented interfaces detection be scoped by file
1920
* Deprecated support for short factories and short configurators in Yaml
2021

2122
Before:
2223
```yaml
2324
services:
24-
my_service:
25-
factory: factory_service:method
25+
my_service:
26+
factory: factory_service:method
2627
```
2728
2829
After:
2930
```yaml
3031
services:
31-
my_service:
32-
factory: ['@factory_service', method]
32+
my_service:
33+
factory: ['@factory_service', method]
3334
```
35+
3436
* Deprecated `tagged` in favor of `tagged_iterator`
3537

3638
Before:
3739
```yaml
3840
services:
39-
App\Handler:
40-
tags: ['app.handler']
41-
4241
App\HandlerCollection:
43-
arguments: [!tagged app.handler]
42+
arguments: [!tagged my_tag]
4443
```
4544

4645
After:
4746
```yaml
4847
services:
49-
App\Handler:
50-
< 10000 span class="pl-s"> tags: ['app.handler']
51-
52-
App\HandlerCollection:
53-
arguments: [!tagged_iterator app.handler]
48+
App\HandlerCollection:
49+
arguments: [!tagged_iterator my_tag]
5450
```
5551

5652
* Passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` is deprecated.
@@ -145,6 +141,7 @@ HttpKernel
145141

146142
As many bundles must be compatible with a range of Symfony versions, the current
147143
directory convention is not deprecated yet, but it will be in the future.
144+
148145
* Deprecated the second and third argument of `KernelInterface::locateResource`
149146
* Deprecated the second and third argument of `FileLocator::__construct`
150147
* Deprecated loading resources from `%kernel.root_dir%/Resources` and `%kernel.root_dir%` as
@@ -283,37 +280,37 @@ TwigBundle
283280
Before (`templates/bundles/TwigBundle/Exception/error.jsonld.twig`):
284281
```twig
285282
{
286-
"@id": "https://example.com",
287-
"@type": "error",
288-
"@context": {
289-
"title": "{{ status_text }}",
290-
"code": {{ status_code }},
291-
"message": "{{ exception.message }}"
292-
}
283+
"@id": "https://example.com",
284+
"@type": "error",
285+
"@context": {
286+
"title": "{{ status_text }}",
287+
"code": {{ status_code }},
288+
"message": "{{ exception.message }}"
289+
}
293290
}
294291
```
295292

296293
After (`App\ErrorRenderer\JsonLdErrorRenderer`):
< 10601 div aria-hidden="true" style="left:-2px" class="position-absolute top-0 d-flex user-select-none DiffLineTableCellParts-module__in-progress-comment-indicator--hx3m3">
297294
```php
298295
class JsonLdErrorRenderer implements ErrorRendererInterface
299296
{
300-
public static function getFormat(): string
301-
{
302-
return 'jsonld';
303-
}
297+
public static function getFormat(): string
298+
{
299+
return 'jsonld';
300+
}
304301
305-
public function render(FlattenException $exception): string
306-
{
307-
return json_encode([
308-
'@id' => 'https://example.com',
309-
'@type' => 'error',
310-
'@context' => [
311-
'title' => $exception->getTitle(),
312-
'code' => $exception->getStatusCode(),
313-
'message' => $exception->getMessage(),
314-
],
315-
]);
316-
}
302+
public function render(FlattenException $exception): string
303+
{
304+
return json_encode([
305+
'@id' => 'https://example.com',
306+
'@type' => 'error',
307+
'@context' => [
308+
'title' => $exception->getTitle(),
309+
'code' => $exception->getStatusCode(),
310+
'message' => $exception->getMessage(),
311+
],
312+
]);
313+
}
317314
}
318315
```
319316

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CHANGELOG
1010
* deprecated `tagged` in favor of `tagged_iterator`
1111
* deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition`
1212
* added support for binding iterable and tagged services
13+
* made singly-implemented interfaces detection be scoped by file
1314

1415
4.3.0
1516
-----

src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,9 @@ final public function __invoke(string $id, string $class = null): ServiceConfigu
139139
{
140140
return $this->set($id, $class);
141141
}
142+
143+
public function __destruct()
144+
{
145+
$this->loader->registerAliasesForSinglyImplementedInterfaces();
146+
}
142147
}

src/Symfony/Component/DependencyInjection/Loader/FileLoader.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ abstract class FileLoader extends BaseFileLoader
2929
protected $container;
3030
protected $isLoadingInstanceof = false;
3131
protected $instanceof = [];
32+
protected $interfaces = [];
33+
protected $singlyImplemented = [];
3234

3335
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
3436
{
@@ -57,12 +59,10 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e
5759
$classes = $this->findClasses($namespace, $resource, (array) $exclude);
5860
// prepare for deep cloning
5961
$serializedPrototype = serialize($prototype);
60-
$interfaces = [];
61-
$singlyImplemented = [];
6262

6363
foreach ($classes as $class => $errorMessage) {
6464
if (interface_exists($class, false)) {
65-
$interfaces[] = $class;
65+
$this->interfaces[] = $class;
6666
} else {
6767
$this->setDefinition($class, $definition = unserialize($serializedPrototype));
6868
if (null !== $errorMessage) {
@@ -71,16 +71,21 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e
7171
continue;
7272
}
7373
foreach (class_implements($class, false) as $interface) {
74-
$singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class;
74+
$this->singlyImplemented[$interface] = isset($this->singlyImplemented[$interface]) ? false : $class;
7575
}
7676
}
7777
}
78-
foreach ($interfaces as $interface) {
79-
if (!empty($singlyImplemented[$interface])) {
80-
$this->container->setAlias($interface, $singlyImplemented[$interface])
81-
->setPublic(false);
78+
}
79+
80+
public function registerAliasesForSinglyImplementedInterfaces()
81+
{
82+
foreach ($this->interfaces as $interface) {
83+
if (!empty($this->singlyImplemented[$interface]) && !$this->container->hasAlias($interface)) {
84+
$this->container->setAlias($interface, $this->singlyImplemented[$interface])->setPublic(false);
8285
}
8386
}
87+
88+
$this->interfaces = $this->singlyImplemented = [];
8489
}
8590

8691
/**

src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ public function load($resource, $type = null)
4141
return include $path;
4242
}, $this, ProtectedPhpFileLoader::class);
4343

44-
$callback = $load($path);
44+
try {
45+
$callback = $load($path);
4546

46-
if (\is_object($callback) && \is_callable($callback)) {
47-
$callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this);
47+
if (\is_object($callback) && \is_callable($callback)) {
48+
$callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this);
49+
}
50+
} finally {
51+
$this->instanceof = [];
52+
$this->registerAliasesForSinglyImplementedInterfaces();
4853
}
4954
}
5055

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public function load($resource, $type = null)
6666
$this->parseDefinitions($xml, $path, $defaults);
6767
} finally {
6868
$this->instanceof = [];
69+
$this->registerAliasesForSinglyImplementedInterfaces();
6970
}
7071
}
7172

src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public function load($resource, $type = null)
150150
$this->parseDefinitions($content, $path);
151151
} finally {
152152
$this->instanceof = [];
153+
$this->registerAliasesForSinglyImplementedInterfaces();
153154
}
154155
}
155156

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter;
4+
5+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface;
6+
7+
class Adapter implements PortInterface
8+
{
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter;
4+
5+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface;
6+
7+
class Adapter implements PortInterface
8+
{
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port;
4+
5+
interface PortInterface
6+
{
7+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
->tag('baz');
1010
$di->load(Prototype::class.'\\', '../Prototype')
1111
->autoconfigure()
12-
->exclude('../Prototype/{OtherDir,BadClasses}')
12+
->exclude('../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}')
1313
->factory('f')
1414
->deprecate('%service_id%')
1515
->args([0])

src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
->tag('baz');
1010
$di->load(Prototype::class.'\\', '../Prototype')
1111
->autoconfigure()
12-
->exclude(['../Prototype/OtherDir', '../Prototype/BadClasses'])
12+
->exclude(['../Prototype/OtherDir', '../Prototype/BadClasses', '../Prototype/SinglyImplementedInterface'])
1313
->factory('f')
1414
->deprecate('%service_id%')
1515
->args([0])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
6+
<services>
7+
<defaults autowire="true" />
8+
9+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
10+
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
11+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\"
12+
resource="../Prototype/SinglyImplementedInterface/AnotherAdapter/*" />
13+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
14+
resource="../Prototype/SinglyImplementedInterface/Port/*" />
15+
</services>
16+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
6+
<services>
7+
<defaults autowire="true" />
8+
9+
<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface"
10+
alias="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter" />
11+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
12+
resource="../Prototype/SinglyImplementedInterface/Port/*" />
13+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
14+
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
15+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\"
16+
resource="../Prototype/SinglyImplementedInterface/AnotherAdapter/*" />
17+
</services>
18+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
6+
<services>
7+
<defaults autowire="true" />
8+
9+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
10+
resource="../Prototype/SinglyImplementedInterface/Port/*" />
11+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
12+
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
13+
<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface"
14+
alias="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter" />
15+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\"
16+
resource="../Prototype/SinglyImplementedInterface/AnotherAdapter/*" />
17+
</services>
18+
</container>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
33
<services>
4-
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir,BadClasses}" />
4+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}" />
55
</services>
66
</container>

src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_prototype_array.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*">
55
<exclude>../Prototype/OtherDir</exclude>
66
<exclude>../Prototype/BadClasses</exclude>
7+
<exclude>../Prototype/SinglyImplementedInterface</exclude>
78
</prototype>
89
</services>
910
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
6+
<services>
7+
<defaults autowire="true" />
8+
9+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
10+
resource="../Prototype/SinglyImplementedInterface/Port/*" />
11+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
12+
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
13+
</services>
14+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
services:
2+
_defaults:
3+
autowire: true
4+
5+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
6+
resource: ../Prototype/SinglyImplementedInterface/Adapter/*
7+
8+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\:
9+
resource: ../Prototype/SinglyImplementedInterface/AnotherAdapter/*
10+
11+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
12+
resource: ../Prototype/SinglyImplementedInterface/Port/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
services:
2+
_defaults:
3+
autowire: true
4+
5+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface:
6+
alias: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter
7+
8+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
9+
resource: ../Prototype/SinglyImplementedInterface/Port/*
10+
11+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
12+
resource: ../Prototype/SinglyImplementedInterface/Adapter/*
13+
14+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\:
15+
resource: ../Prototype/SinglyImplementedInterface/AnotherAdapter/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
services:
2+
_defaults:
3+
autowire: true
4+
5+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
6+
resource: ../Prototype/SinglyImplementedInterface/Port/*
7+
8+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
9+
resource: ../Prototype/SinglyImplementedInterface/Adapter/*
10+
11+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface:
12+
alias: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter
13+
14+
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\:
15+
resource: ../Prototype/SinglyImplementedInterface/AnotherAdapter/*

0 commit comments

Comments
 (0)
0