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

Skip to content

Commit bec3890

Browse files
daniel-iwaniecnicolas-grekas
authored andcommitted
[DI] scope singly-implemented interfaces detection by file
1 parent c1bca29 commit bec3890

24 files changed

+278
-12
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ final public function parameters(): ParametersConfigurator
7373

7474
final public function services(): ServicesConfigurator
F438
7575
{
76+
$this->loader->resetBeforeConfiguringServices();
77+
7678
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount);
7779
}
7880
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ final public function alias(string $id, string $referencedId): AliasConfigurator
105105
$ref = static::processValue($referencedId, true);
106106
$alias = new Alias((string) $ref, $this->defaults->isPublic());
107107
$this->container->setAlias($id, $alias);
108+
$this->loader->removeSinglyImplementedAlias((string) $ref);
108109

109110
return new AliasConfigurator($this, $alias);
110111
}

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

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

3336
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
3437
{
@@ -57,12 +60,10 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e
5760
$classes = $this->findClasses($namespace, $resource, (array) $exclude);
5861
// prepare for deep cloning
5962
$serializedPrototype = serialize($prototype);
60-
$interfaces = [];
61-
$singlyImplemented = [];
6263

6364
foreach ($classes as $class => $errorMessage) {
6465
if (interface_exists($class, false)) {
65-
$interfaces[] = $class;
66+
$this->interfaces[] = $class;
6667
} else {
6768
$this->setDefinition($class, $definition = unserialize($serializedPrototype));
6869
if (null !== $errorMessage) {
@@ -71,14 +72,17 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e
7172
continue;
7273
}
7374
foreach (class_implements($class, false) as $interface) {
74-
$singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class;
75+
$this->singlyImplemented[$interface] = isset($this->singlyImplemented[$interface]) ? false : $class;
7576
}
7677
}
7778
}
78-
foreach ($interfaces as $interface) {
79-
if (!empty($singlyImplemented[$interface])) {
80-
$this->container->setAlias($interface, $singlyImplemented[$interface])
81-
->setPublic(false);
79+
80+
foreach ($this->interfaces as $interface) {
81+
if (!empty($this->singlyImplemented[$interface]) && !$this->container->hasAlias($interface)) {
82+
$this->container->setAlias($interface, $this->singlyImplemented[$interface])->setPublic(false);
83+
$this->singlyImplementedAliases[$interface] = true;
84+
} elseif ($this->singlyImplementedAliases[$interface] ?? false) {
85+
$this->container->removeAlias($interface);
8286
}
8387
}
8488
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ public function supports($resource, $type = null)
6363

6464
return 'php' === $type;
6565
}
66+
67+
public function resetBeforeConfiguringServices(): void
68+
{
69+
$this->interfaces = [];
70+
$this->singlyImplemented = [];
71+
$this->singlyImplementedAliases = [];
72+
}
73+
74+
public function removeSinglyImplementedAlias(string $alias): void
75+
{
76+
unset($this->singlyImplementedAliases[$alias]);
77+
}
6678
}
6779

6880
/**

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ public function load($resource, $type = null)
6666
$this->parseDefinitions($xml, $path, $defaults);
6767
} finally {
6868
$this->instanceof = [];
69+
$this->interfaces = [];
70+
$this->singlyImplemented = [];
71+
$this->singlyImplementedAliases = [];
6972
}
7073
}
7174

@@ -194,6 +197,7 @@ private function parseDefinition(\DOMElement $service, string $file, array $defa
194197
$this->validateAlias($service, $file);
195198

196199
$this->container->setAlias((string) $service->getAttribute('id'), $alias = new Alias($alias));
200+
unset($this->singlyImplementedAliases[(string) $service->getAttribute('id')]);
197201
if ($publicAttr = $service->getAttribute('public')) {
198202
$alias->setPublic(XmlUtils::phpize($publicAttr));
199203
} elseif (isset($defaults['public'])) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ public function load($resource, $type = null)
150150
$this->parseDefinitions($content, $path);
151151
} finally {
152152
$this->instanceof = [];
153+
$this->interfaces = [];
154+
$this->singlyImplemented = [];
155+
$this->singlyImplementedAliases = [];
153156
}
154157
}
155158

@@ -318,6 +321,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa
318321

319322
if (\is_string($service) && 0 === strpos($service, '@')) {
320323
$this->container->setAlias($id, $alias = new Alias(substr($service, 1)));
324+
unset($this->singlyImplementedAliases[$id]);
321325
if (isset($defaults['public'])) {
322326
$alias->setPublic($defaults['public']);
323327
}
@@ -341,6 +345,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa
341345

342346
if (isset($service['alias'])) {
343347
$this->container->setAlias($id, $alias = new Alias($service['alias']));
348+
unset($this->singlyImplementedAliases[$id]);
344349
if (\array_key_exists('public', $service)) {
345350
$alias->setPublic($service['public']);
346351
} elseif (isset($defaults['public'])) {
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/*
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
services:
22
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\:
33
resource: ../Prototype
4-
exclude: '../Prototype/{OtherDir,BadClasses}'
4+
exclude: '../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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/*

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ public function testPrototype()
652652
[
653653
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true,
654654
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true,
655+
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'SinglyImplementedInterface') => true,
655656
]
656657
);
657658
$this->assertContains((string) $globResource, $resources);
@@ -684,6 +685,7 @@ public function testPrototypeExcludeWithArray()
684685
[
685686
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true,
686687
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true,
688+
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'SinglyImplementedInterface') => true,
687689
]
688690
);
689691
$this->assertContains((string) $globResource, $resources);
@@ -901,4 +903,50 @@ public function testOverriddenDefaultsBindings()
901903

902904
$this->assertSame('overridden', $container->get('bar')->quz);
903905
}
906+
907+
public function testSinglyImplementedInterfacesInMultipleResources()
908+
{
909+
$container = new ContainerBuilder();
910+
911+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
912+
$loader->load('singly_implemented_interface_in_multiple_resources.xml');
913+
914+
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
915+
916+
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
917+
}
918+
919+
public function testNotSinglyImplementedInterfacesInMultipleResources()
920+
{
921+
$container = new ContainerBuilder();
922+
923+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
924+
$loader->load('not_singly_implemented_interface_in_multiple_resources.xml');
925+
926+
$this->assertFalse($container->hasAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class));
927+
}
928+
929+
public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviouslyRegisteredAlias()
930+
{
931+
$container = new ContainerBuilder();
932+
933+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
934+
$loader->load('not_singly_implemented_interface_in_multiple_resources_with_previously_registered_alias.xml');
935+
936+
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
937+
938+
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
939+
}
940+
941+
public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviouslyRegisteredAlias2()
942+
{
943+
$container = new ContainerBuilder();
944+
945+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
946+
$loader->load('not_singly_implemented_interface_in_multiple_resources_with_previously_registered_alias2.xml');
947+
948+
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
949+
950+
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
951+
}
904952
}

0 commit comments

Comments
 (0)
0