10000 [DependencyInjection] Add `#[Factory]` and `factory` option to `#[Aut… · symfony/symfony@4df47ec · GitHub
[go: up one dir, main page]

Skip to content

Commit 4df47ec

Browse files
[DependencyInjection] Add #[Factory] and factory option to #[Autoconfigure]
1 parent cb39a7f commit 4df47ec

File tree

9 files changed

+146
-1
lines changed

9 files changed

+146
-1
lines changed

src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function __construct(
2929
public ?bool $autowire = null,
3030
public ?array $properties = null,
3131
public array|string|null $configurator = null,
32+
public array|string|null $factory = null,
3233
) {
3334
}
3435
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 configure the factory to use on
16+
* a service.
17+
*
18+
* @author Alexandre Daubois <alex.daubois@gmail.com>
19+
*/
20+
#[\Attribute(\Attribute::TARGET_CLASS)]
21+
final class Factory extends Autoconfigure
22+
{
23+
public function __construct(
24+
array|string $factory,
25+
array $bind = null,
26+
) {
27+
parent::__construct(bind: $bind, factory: $factory);
28+
}
29+
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ CHANGELOG
1818
* Add support for autowiring services as closures using `#[AutowireCallable]` or `#[AutowireServiceClosure]`
1919
* Deprecate `#[MapDecorated]`, use `#[AutowireDecorated]` instead
2020
* Deprecate the `@required` annotation, use the `Symfony\Contracts\Service\Attribute\Required` attribute instead
21+
* Add `#[Factory]` and `factory` option to `#[Autoconfigure]`
2122

2223
6.2
2324
---

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

1414
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
15+
use Symfony\Component\DependencyInjection\Attribute\Factory;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Definition;
1718
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
@@ -57,8 +58,15 @@ private static function registerForAutoconfiguration(ContainerBuilder $container
5758
$yamlLoader = $parseDefinitions->getDeclaringClass()->newInstanceWithoutConstructor();
5859

5960
self::$registerForAutoconfiguration = static function (ContainerBuilder $container, \ReflectionClass $class, \ReflectionAttribute $attribute) use ($parseDefinitions, $yamlLoader) {
60-
$attribute = (array) $attribute->newInstance();
61+
$attribute = $attribute->newInstance();
6162

63+
if ($attribute instanceof Factory) {
64+
if (\is_string($attribute->factory) && $class->hasMethod($attribute->factory)) {
65+
$attribute->factory = [null, $attribute->factory];
66+
}
67+
}
68+
69+
$attribute = (array) $attribute;
6270
foreach ($attribute['tags'] ?? [] as $i => $tag) {
6371
if (\is_array($tag) && [0] === array_keys($tag)) {
6472
$attribute['tags'][$i] = [$class->name => $tag[0]];

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class YamlFileLoader extends FileLoader
9696
'tags' => 'tags',
9797
'autowire' => 'autowire',
9898
'bind' => 'bind',
99+
'factory' => 'factory',
99100
];
100101

101102
private const DEFAULTS_KEYWORDS = [

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use Symfony\Component\DependencyInjection\Reference;
2020
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureAttributed;
2121
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfiguredInterface;
22+
use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryAttributeService;
23+
use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryAttributeServiceAsString;
2224
use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists;
2325

2426
class RegisterAutoconfigureAttributesPassTest extends TestCase
@@ -47,6 +49,7 @@ public function testProcess()
4749
->addTag('another_tag', ['attr' => 234])
4850
->addMethodCall('setBar', [2, 3])
4951
->setBindings(['$bar' => $argument])
52+
->setFactory([null, 'create'])
5053
;
5154
$this->assertEquals([AutoconfigureAttributed::class => $expected], $container->getAutoconfiguredInstanceof());
5255
}
@@ -88,4 +91,38 @@ public function testMissingParent()
8891

8992
$this->addToAssertionCount(1);
9093
}
94+
95+
public function testFactoryAttribute()
96+
{
97+
$container = new ContainerBuilder();
98+
$container->register('foo', FactoryAttributeService::class)
99+
->setAutoconfigured(true);
100+
101+
$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/FactoryAttributeService.php'));
102+
103+
(new RegisterAutoconfigureAttributesPass())->process($container);
104+
105+
$expected = (new ChildDefinition(''))
106+
->setFactory([null, 'create'])
107+
->setBindings(['$foo' => $argument])
108+
;
109+
$this->assertEquals([FactoryAttributeService::class => $expected], $container->getAutoconfiguredInstanceof());
110+
}
111+
112+
public function testFactoryAttributeAsString()
113+
{
114+
$container = new ContainerBuilder();
115+
$container->register('foo', FactoryAttributeServiceAsString::class)
116+
->setAutoconfigured(true);
117+
118+
$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/FactoryAttributeServiceAsString.php'));
119+
120+
(new RegisterAutoconfigureAttributesPass())->process($container);
121+
122+
$expected = (new ChildDefinition(''))
123+
->setFactory([null, 'create'])
124+
->setBindings(['$foo' => $argument])
125+
;
126+
$this->assertEquals([FactoryAttributeServiceAsString::class => $expected], $container->getAutoconfiguredInstanceof());
127+
}
91128
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureAttributed.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
bind: [
2424
'$bar' => 1,
2525
],
26+
factory: [
27+
null,
28+
'create'
29+
]
2630
)]
2731
class AutoconfigureAttributed
2832
{
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Tests\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\Attribute\Factory;
15+
16+
#[Factory([null, 'create'], ['$foo' => 'foo'])]
17+
class FactoryAttributeService
18+
{
19+
public function __construct(private readonly string $bar)
20+
{
21+
}
22+
23+
public function getBar(): string
24+
{
25+
return $this->bar;
26+
}
27+
28+
public static function create(string $foo): static
29+
{
30+
return new self($foo);
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Tests\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\Attribute\Factory;
15+
16+
#[Factory('create', ['$foo' => 'foo'])]
17+
class FactoryAttributeServiceAsString
18+
{
19+
public function __construct(private readonly string $bar = 'test')
20+
{
21+
}
22+
23+
public function getBar(): string
24+
{
25+
return $this->bar;
26+
}
27+
28+
public static function create(string $foo): static
29+
{
30+
return new self($foo);
31+
}
32+
}

0 commit comments

Comments
 (0)
0