Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fe63d39

Browse files
alanpoulainchalasr
authored andcommitted
feat(di): add AsAlias attribute
1 parent a034e6d commit fe63d39

File tree

12 files changed

+210
-2
lines changed

12 files changed

+210
-2
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Attribute;
13+
14+
/**
15+
* An attribute to tell under which alias a service should be registered or to use the implemented interface if no parameter is given.
16+
*
17+
* @author Alan Poulain <[email protected]>
18+
*/
19+
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
20+
final class AsAlias
21+
{
22+
public function __construct(
23+
public ?string $id = null,
24+
public bool $public = false,
25+
) {
26+
}
27+
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CHANGELOG
1111
* Deprecate undefined and numeric keys with `service_locator` config
1212
* Fail if Target attribute does not exist during compilation
1313
* Enable deprecating parameters with `ContainerBuilder::deprecateParameter()`
14+
* Add `#[AsAlias]` attribute to tell under which alias a service should be registered or to use the implemented interface if no parameter is given
1415

1516
6.2
1617
---

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
1818
use Symfony\Component\Config\Loader\Loader;
1919
use Symfony\Component\Config\Resource\GlobResource;
20+
use Symfony\Component\DependencyInjection\Alias;
21+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
2022
use Symfony\Component\DependencyInjection\Attribute\When;
2123
use Symfony\Component\DependencyInjection\ChildDefinition;
2224
use Symfony\Component\DependencyInjection\Compiler\RegisterAutoconfigureAttributesPass;
2325
use Symfony\Component\DependencyInjection\ContainerBuilder;
2426
use Symfony\Component\DependencyInjection\Definition;
2527
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
28+
use Symfony\Component\DependencyInjection\Exception\LogicException;
2629

2730
/**
2831
* FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -38,6 +41,8 @@ abstract class FileLoader extends BaseFileLoader
3841
protected $instanceof = [];
3942
protected $interfaces = [];
4043
protected $singlyImplemented = [];
44+
/** @var array<string, Alias> */
45+
protected $aliases = [];
4146
protected $autoRegisterAliasesForSinglyImplementedInterfaces = true;
4247

4348
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, string $env = null)
@@ -140,12 +145,37 @@ public function registerClasses(Definition $prototype, string $namespace, string
140145

141146
continue;
142147
}
148+
$interfaces = [];
143149
foreach (class_implements($class, false) as $interface) {
144150
$this->singlyImplemented[$interface] = ($this->singlyImplemented[$interface] ?? $class) !== $class ? false : $class;
151+
$interfaces[] = $interface;
152+
}
153+
154+
if (!$autoconfigureAttributes) {
155+
continue;
156+
}
157+
$r = $this->container->getReflectionClass($class);
158+
$defaultAlias = 1 === \count($interfaces) ? $interfaces[0] : null;
159+
foreach ($r->getAttributes(AsAlias::class) as $attr) {
160+
/** @var AsAlias $attribute */
161+
$attribute = $attr->newInstance();
162+
$alias = $attribute->id ?? $defaultAlias;
163+
$public = $attribute->public;
164+
if (null === $alias) {
165+
throw new LogicException(sprintf('Alias cannot be automatically determined for class "%s". If you have used the #[AsAlias] attribute with a class implementing multiple interfaces, add the interface you want to alias to the first parameter of #[AsAlias].', $class));
166+
}
167+
if (isset($this->aliases[$alias])) {
168+
throw new LogicException(sprintf('The "%s" alias has already been defined with the #[AsAlias] attribute in "%s".', $alias, $this->aliases[$alias]));
169+
}
170+
$this->aliases[$alias] = new Alias($class, $public);
145171
}
146172
}
147173
}
148174

175+
foreach ($this->aliases as $alias => $aliasDefinition) {
176+
$this->container->setAlias($alias, $aliasDefinition);
177+
}
178+
149179
if ($this->autoRegisterAliasesForSinglyImplementedInterfaces) {
150180
$this->registerAliasesForSinglyImplementedInterfaces();
151181
}
@@ -157,12 +187,12 @@ public function registerClasses(Definition $prototype, string $namespace, string
157187
public function registerAliasesForSinglyImplementedInterfaces()
158188
{
159189
foreach ($this->interfaces as $interface) {
160-
if (!empty($this->singlyImplemented[$interface]) && !$this->container->has($interface)) {
190+
if (!empty($this->singlyImplemented[$interface]) && !isset($this->aliases[$interface]) && !$this->container->has($interface)) {
161191
$this->container->setAlias($interface, $this->singlyImplemented[$interface]);
162192
}
163193
}
164194

165-
$this->interfaces = $this->singlyImplemented = [];
195+
$this->interfaces = $this->singlyImplemented = $this->aliases = [];
166196
}
167197

168198
/**
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
interface AliasBarInterface
6+
{
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
interface AliasFooInterface
6+
{
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
6+
7+
#[AsAlias(id: AliasFooInterface::class)]
8+
class WithAsAlias
9+
{
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
6+
7+
#[AsAlias(id: AliasFooInterface::class)]
8+
class WithAsAliasDuplicate
9+
{
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
6+
7+
#[AsAlias(id: AliasBarInterface::class)]
8+
class WithAsAliasIdMultipleInterface implements AliasFooInterface, AliasBarInterface
9+
{
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
6+
7+
#[AsAlias]
8+
class WithAsAliasInterface implements AliasFooInterface
9+
{
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias;
4+
5+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
6+
7+
#[AsAlias(id: AliasFooInterface::class, public: true)]
8+
#[AsAlias(id: 'some-alias')]
9+
class WithAsAliasMultiple
10+
{
11+
}

0 commit comments

Comments
 (0)