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
-------

c1f39709ff [DI] add FileLoader::registerAliasesForSinglyImplementedInterfaces()
bec38900d8 [DI] scope singly-implemented interfaces detection by file
This commit is contained in:
Fabien Potencier 2019-09-25 21:03:45 +02:00
commit db5cf1a83e
26 changed files with 310 additions and 51 deletions

View File

@ -16,41 +16,37 @@ Debug
DependencyInjection DependencyInjection
------------------- -------------------
* Made singly-implemented interfaces detection be scoped by file
* Deprecated support for short factories and short configurators in Yaml * Deprecated support for short factories and short configurators in Yaml
Before: Before:
```yaml ```yaml
services: services:
my_service: my_service:
factory: factory_service:method factory: factory_service:method
``` ```
After: After:
```yaml ```yaml
services: services:
my_service: my_service:
factory: ['@factory_service', method] factory: ['@factory_service', method]
``` ```
* Deprecated `tagged` in favor of `tagged_iterator` * Deprecated `tagged` in favor of `tagged_iterator`
Before: Before:
```yaml ```yaml
services: services:
App\Handler:
tags: ['app.handler']
App\HandlerCollection: App\HandlerCollection:
arguments: [!tagged app.handler] arguments: [!tagged my_tag]
``` ```
After: After:
```yaml ```yaml
services: services:
App\Handler: App\HandlerCollection:
tags: ['app.handler'] arguments: [!tagged_iterator my_tag]
App\HandlerCollection:
arguments: [!tagged_iterator app.handler]
``` ```
* Passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` is deprecated. * Passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` is deprecated.
@ -145,6 +141,7 @@ HttpKernel
As many bundles must be compatible with a range of Symfony versions, the current As many bundles must be compatible with a range of Symfony versions, the current
directory convention is not deprecated yet, but it will be in the future. directory convention is not deprecated yet, but it will be in the future.
* Deprecated the second and third argument of `KernelInterface::locateResource` * Deprecated the second and third argument of `KernelInterface::locateResource`
* Deprecated the second and third argument of `FileLocator::__construct` * Deprecated the second and third argument of `FileLocator::__construct`
* Deprecated loading resources from `%kernel.root_dir%/Resources` and `%kernel.root_dir%` as * Deprecated loading resources from `%kernel.root_dir%/Resources` and `%kernel.root_dir%` as
@ -283,13 +280,13 @@ TwigBundle
Before (`templates/bundles/TwigBundle/Exception/error.jsonld.twig`): Before (`templates/bundles/TwigBundle/Exception/error.jsonld.twig`):
```twig ```twig
{ {
"@id": "https://example.com", "@id": "https://example.com",
"@type": "error", "@type": "error",
"@context": { "@context": {
"title": "{{ status_text }}", "title": "{{ status_text }}",
"code": {{ status_code }}, "code": {{ status_code }},
"message": "{{ exception.message }}" "message": "{{ exception.message }}"
} }
} }
``` ```
@ -297,23 +294,23 @@ TwigBundle
```php ```php
class JsonLdErrorRenderer implements ErrorRendererInterface class JsonLdErrorRenderer implements ErrorRendererInterface
{ {
public static function getFormat(): string public static function getFormat(): string
{ {
return 'jsonld'; return 'jsonld';
} }
public function render(FlattenException $exception): string public function render(FlattenException $exception): string
{ {
return json_encode([ return json_encode([
'@id' => 'https://example.com', '@id' => 'https://example.com',
'@type' => 'error', '@type' => 'error',
'@context' => [ '@context' => [
'title' => $exception->getTitle(), 'title' => $exception->getTitle(),
'code' => $exception->getStatusCode(), 'code' => $exception->getStatusCode(),
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
], ],
]); ]);
} }
} }
``` ```

View File

@ -10,6 +10,7 @@ CHANGELOG
* deprecated `tagged` in favor of `tagged_iterator` * deprecated `tagged` in favor of `tagged_iterator`
* deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` * deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition`
* added support for binding iterable and tagged services * added support for binding iterable and tagged services
* made singly-implemented interfaces detection be scoped by file
4.3.0 4.3.0
----- -----

View File

@ -139,4 +139,9 @@ class ServicesConfigurator extends AbstractConfigurator
{ {
return $this->set($id, $class); return $this->set($id, $class);
} }
public function __destruct()
{
$this->loader->registerAliasesForSinglyImplementedInterfaces();
}
} }

View File

@ -29,6 +29,8 @@ abstract class FileLoader extends BaseFileLoader
protected $container; protected $container;
protected $isLoadingInstanceof = false; protected $isLoadingInstanceof = false;
protected $instanceof = []; protected $instanceof = [];
protected $interfaces = [];
protected $singlyImplemented = [];
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator) public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
{ {
@ -57,12 +59,10 @@ abstract class FileLoader extends BaseFileLoader
$classes = $this->findClasses($namespace, $resource, (array) $exclude); $classes = $this->findClasses($namespace, $resource, (array) $exclude);
// prepare for deep cloning // prepare for deep cloning
$serializedPrototype = serialize($prototype); $serializedPrototype = serialize($prototype);
$interfaces = [];
$singlyImplemented = [];
foreach ($classes as $class => $errorMessage) { foreach ($classes as $class => $errorMessage) {
if (interface_exists($class, false)) { if (interface_exists($class, false)) {
$interfaces[] = $class; $this->interfaces[] = $class;
} else { } else {
$this->setDefinition($class, $definition = unserialize($serializedPrototype)); $this->setDefinition($class, $definition = unserialize($serializedPrototype));
if (null !== $errorMessage) { if (null !== $errorMessage) {
@ -71,16 +71,21 @@ abstract class FileLoader extends BaseFileLoader
continue; continue;
} }
foreach (class_implements($class, false) as $interface) { foreach (class_implements($class, false) as $interface) {
$singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class; $this->singlyImplemented[$interface] = isset($this->singlyImplemented[$interface]) ? false : $class;
} }
} }
} }
foreach ($interfaces as $interface) { }
if (!empty($singlyImplemented[$interface])) {
$this->container->setAlias($interface, $singlyImplemented[$interface]) public function registerAliasesForSinglyImplementedInterfaces()
->setPublic(false); {
foreach ($this->interfaces as $interface) {
if (!empty($this->singlyImplemented[$interface]) && !$this->container->hasAlias($interface)) {
$this->container->setAlias($interface, $this->singlyImplemented[$interface])->setPublic(false);
} }
} }
$this->interfaces = $this->singlyImplemented = [];
} }
/** /**

View File

@ -41,10 +41,15 @@ class PhpFileLoader extends FileLoader
return include $path; return include $path;
}, $this, ProtectedPhpFileLoader::class); }, $this, ProtectedPhpFileLoader::class);
$callback = $load($path); try {
$callback = $load($path);
if (\is_object($callback) && \is_callable($callback)) { if (\is_object($callback) && \is_callable($callback)) {
$callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this); $callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this);
}
} finally {
$this->instanceof = [];
$this->registerAliasesForSinglyImplementedInterfaces();
} }
} }

View File

@ -66,6 +66,7 @@ class XmlFileLoader extends FileLoader
$this->parseDefinitions($xml, $path, $defaults); $this->parseDefinitions($xml, $path, $defaults);
} finally { } finally {
$this->instanceof = []; $this->instanceof = [];
$this->registerAliasesForSinglyImplementedInterfaces();
} }
} }

View File

@ -150,6 +150,7 @@ class YamlFileLoader extends FileLoader
$this->parseDefinitions($content, $path); $this->parseDefinitions($content, $path);
} finally { } finally {
$this->instanceof = []; $this->instanceof = [];
$this->registerAliasesForSinglyImplementedInterfaces();
} }
} }

View File

@ -0,0 +1,9 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface;
class Adapter implements PortInterface
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface;
class Adapter implements PortInterface
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port;
interface PortInterface
{
}

View File

@ -9,7 +9,7 @@ return function (ContainerConfigurator $c) {
->tag('baz'); ->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype') $di->load(Prototype::class.'\\', '../Prototype')
->autoconfigure() ->autoconfigure()
->exclude('../Prototype/{OtherDir,BadClasses}') ->exclude('../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}')
->factory('f') ->factory('f')
->deprecate('%service_id%') ->deprecate('%service_id%')
->args([0]) ->args([0])

View File

@ -9,7 +9,7 @@ return function (ContainerConfigurator $c) {
->tag('baz'); ->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype') $di->load(Prototype::class.'\\', '../Prototype')
->autoconfigure() ->autoconfigure()
->exclude(['../Prototype/OtherDir', '../Prototype/BadClasses']) ->exclude(['../Prototype/OtherDir', '../Prototype/BadClasses', '../Prototype/SinglyImplementedInterface'])
->factory('f') ->factory('f')
->deprecate('%service_id%') ->deprecate('%service_id%')
->args([0]) ->args([0])

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<services>
<defaults autowire="true" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\"
resource="../Prototype/SinglyImplementedInterface/AnotherAdapter/*" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
resource="../Prototype/SinglyImplementedInterface/Port/*" />
</services>
</container>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<services>
<defaults autowire="true" />
<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface"
alias="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
resource="../Prototype/SinglyImplementedInterface/Port/*" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\"
resource="../Prototype/SinglyImplementedInterface/AnotherAdapter/*" />
</services>
</container>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<services>
<defaults autowire="true" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
resource="../Prototype/SinglyImplementedInterface/Port/*" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface"
alias="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\"
resource="../Prototype/SinglyImplementedInterface/AnotherAdapter/*" />
</services>
</container>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<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"> <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">
<services> <services>
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir,BadClasses}" /> <prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}" />
</services> </services>
</container> </container>

View File

@ -4,6 +4,7 @@
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*"> <prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*">
<exclude>../Prototype/OtherDir</exclude> <exclude>../Prototype/OtherDir</exclude>
<exclude>../Prototype/BadClasses</exclude> <exclude>../Prototype/BadClasses</exclude>
<exclude>../Prototype/SinglyImplementedInterface</exclude>
</prototype> </prototype>
</services> </services>
</container> </container>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<services>
<defaults autowire="true" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\"
resource="../Prototype/SinglyImplementedInterface/Port/*" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\"
resource="../Prototype/SinglyImplementedInterface/Adapter/*" />
</services>
</container>

View File

@ -0,0 +1,12 @@
services:
_defaults:
autowire: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
resource: ../Prototype/SinglyImplementedInterface/Adapter/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\:
resource: ../Prototype/SinglyImplementedInterface/AnotherAdapter/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
resource: ../Prototype/SinglyImplementedInterface/Port/*

View File

@ -0,0 +1,15 @@
services:
_defaults:
autowire: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface:
alias: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
resource: ../Prototype/SinglyImplementedInterface/Port/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
resource: ../Prototype/SinglyImplementedInterface/Adapter/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\:
resource: ../Prototype/SinglyImplementedInterface/AnotherAdapter/*

View File

@ -0,0 +1,15 @@
services:
_defaults:
autowire: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
resource: ../Prototype/SinglyImplementedInterface/Port/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
resource: ../Prototype/SinglyImplementedInterface/Adapter/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\PortInterface:
alias: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\Adapter
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\AnotherAdapter\:
resource: ../Prototype/SinglyImplementedInterface/AnotherAdapter/*

View File

@ -1,4 +1,4 @@
services: services:
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\:
resource: ../Prototype resource: ../Prototype
exclude: '../Prototype/{OtherDir,BadClasses}' exclude: '../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}'

View File

@ -0,0 +1,9 @@
services:
_defaults:
autowire: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Port\:
resource: ../Prototype/SinglyImplementedInterface/Port/*
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\SinglyImplementedInterface\Adapter\:
resource: ../Prototype/SinglyImplementedInterface/Adapter/*

View File

@ -240,4 +240,10 @@ class TestFileLoader extends FileLoader
{ {
return false; return false;
} }
public function registerClasses(Definition $prototype, $namespace, $resource, $exclude = null)
{
parent::registerClasses($prototype, $namespace, $resource, $exclude);
$this->registerAliasesForSinglyImplementedInterfaces();
}
} }

View File

@ -652,6 +652,7 @@ class XmlFileLoaderTest extends TestCase
[ [
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true, str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true,
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true, str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true,
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'SinglyImplementedInterface') => true,
] ]
); );
$this->assertContains((string) $globResource, $resources); $this->assertContains((string) $globResource, $resources);
@ -684,6 +685,7 @@ class XmlFileLoaderTest extends TestCase
[ [
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true, str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true,
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true, str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true,
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'SinglyImplementedInterface') => true,
] ]
); );
$this->assertContains((string) $globResource, $resources); $this->assertContains((string) $globResource, $resources);
@ -901,4 +903,50 @@ class XmlFileLoaderTest extends TestCase
$this->assertSame('overridden', $container->get('bar')->quz); $this->assertSame('overridden', $container->get('bar')->quz);
} }
public function testSinglyImplementedInterfacesInMultipleResources()
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('singly_implemented_interface_in_multiple_resources.xml');
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
public function testNotSinglyImplementedInterfacesInMultipleResources()
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('not_singly_implemented_interface_in_multiple_resources.xml');
$this->assertFalse($container->hasAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class));
}
public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviouslyRegisteredAlias()
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('not_singly_implemented_interface_in_multiple_resources_with_previously_registered_alias.xml');
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviouslyRegisteredAlias2()
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('not_singly_implemented_interface_in_multiple_resources_with_previously_registered_alias2.xml');
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
} }

View File

@ -421,6 +421,7 @@ class YamlFileLoaderTest extends TestCase
false, [ false, [
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true, str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'BadClasses') => true,
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true, str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'OtherDir') => true,
str_replace(\DIRECTORY_SEPARATOR, '/', $prototypeRealPath.\DIRECTORY_SEPARATOR.'SinglyImplementedInterface') => true,
] ]
); );
$this->assertContains((string) $globResource, $resources); $this->assertContains((string) $globResource, $resources);
@ -840,4 +841,50 @@ class YamlFileLoaderTest extends TestCase
$this->assertInstanceOf(TaggedIteratorArgument::class, $iteratorArgument); $this->assertInstanceOf(TaggedIteratorArgument::class, $iteratorArgument);
$this->assertNull($iteratorArgument->getIndexAttribute()); $this->assertNull($iteratorArgument->getIndexAttribute());
} }
public function testSinglyImplementedInterfacesInMultipleResources()
{
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('singly_implemented_interface_in_multiple_resources.yml');
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
public function testNotSinglyImplementedInterfacesInMultipleResources()
{
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('not_singly_implemented_interface_in_multiple_resources.yml');
$this->assertFalse($container->hasAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class));
}
public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviouslyRegisteredAlias()
{
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('not_singly_implemented_interface_in_multiple_resources_with_previously_registered_alias.yml');
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviouslyRegisteredAlias2()
{
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('not_singly_implemented_interface_in_multiple_resources_with_previously_registered_alias2.yml');
$alias = $container->getAlias(Prototype\SinglyImplementedInterface\Port\PortInterface::class);
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
} }