Skip to content

Commit c34ef1b

Browse files
committed
[DependencyInjection] add factory argument in the #[Autoconfigure] attribute and allow instanceof factory
1 parent 6e2fcec commit c34ef1b

18 files changed

+312
-4
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Autoconfigure
3030
* @param array<string, mixed>|null $properties The properties to define when creating the service
3131
* @param array{string, string}|string|null $configurator A PHP function, reference or an array containing a class/reference and a method to call after the service is fully initialized
3232
* @param string|null $constructor The public static method to use to instantiate the service
33+
* @param array<string|null, string|null>|string|null $factory The factory that defines how to create the service
3334
*/
3435
public function __construct(
3536
public ?array $tags = null,
@@ -42,6 +43,7 @@ public function __construct(
4243
public ?array $properties = null,
4344
public array|string|null $configurator = null,
4445
public ?string $constructor = null,
46+
public array|string|null $factory = null,
4547
) {
4648
}
4749
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Allow `#[AsAlias]` to be extended
88
* Add argument `$target` to `ContainerBuilder::registerAliasForArgument()`
9+
* Add support for a `factory` argument in the `#[Autoconfigure]` attribute to define service instantiation via a factory. Also enabled the use of `factory` within `instanceof` conditionals
910

1011
7.3
1112
---

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class InstanceofConfigurator extends AbstractServiceConfigurator
2323
use Traits\CallTrait;
2424
use Traits\ConfiguratorTrait;
2525
use Traits\ConstructorTrait;
26+
use Traits\FactoryTrait;
2627
use Traits\LazyTrait;
2728
use Traits\PropertyTrait;
2829
use Traits\PublicTrait;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class YamlFileLoader extends FileLoader
100100
'autowire' => 'autowire',
101101
'bind' => 'bind',
102102
'constructor' => 'constructor',
103+
'factory' => 'factory',
103104
];
104105

105106
private const DEFAULTS_KEYWORDS = [

src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
<xsd:complexType name="instanceof">
176176
<xsd:choice maxOccurs="unbounded">
177177
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
178+
<xsd:element name="factory" type="factory" minOccurs="0" maxOccurs="1" />
178179
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
179180
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
180181
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedOverwrite;
2727
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedProperties;
2828
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedTag;
29+
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithExpressionFactory;
30+
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithInstanceExternalFactory;
31+
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithInvokableFactory;
32+
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithStaticExternalFactory;
33+
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithStaticSelfFactory;
34+
use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy;
2935
use Symfony\Component\DependencyInjection\Tests\Fixtures\LazyAutoconfigured;
3036
use Symfony\Component\DependencyInjection\Tests\Fixtures\LazyLoaded;
3137
use Symfony\Component\DependencyInjection\Tests\Fixtures\MultipleAutoconfigureAttributed;
@@ -208,6 +214,88 @@ public function testStaticConstructor()
208214
$this->assertEquals([StaticConstructorAutoconfigure::class => $expected], $container->getAutoconfiguredInstanceof());
209215
}
210216

217+
public function testAutoconfigureWithStaticSelfFactory()
218+
{
219+
$container = new ContainerBuilder();
220+
$container->register('foo', AutoconfigureWithStaticSelfFactory::class)
221+
->setAutoconfigured(true);
222+
223+
$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithStaticSelfFactory.php'));
224+
225+
(new RegisterAutoconfigureAttributesPass())->process($container);
226+
227+
$expected = (new ChildDefinition(''))
228+
->setFactory([null, 'create'])
229+
->setBindings(['$foo' => $argument])
230+
;
231+
$this->assertEquals([AutoconfigureWithStaticSelfFactory::class => $expected], $container->getAutoconfiguredInstanceof());
232+
}
233+
234+
public function testAutoconfigureWithStaticExternalFactory()
235+
{
236+
$container = new ContainerBuilder();
237+
$container->register('foo', AutoconfigureWithStaticExternalFactory::class)
238+
->setAutoconfigured(true);
239+
240+
$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithStaticExternalFactory.php'));
241+
242+
(new RegisterAutoconfigureAttributesPass())->process($container);
243+
244+
$expected = (new ChildDefinition(''))
245+
->setFactory([FactoryDummy::class, 'create'])
246+
->setBindings(['$foo' => $argument])
247+
;
248+
$this->assertEquals([AutoconfigureWithStaticExternalFactory::class => $expected], $container->getAutoconfiguredInstanceof());
249+
}
250+
251+
public function testAutoconfigureWithInstanceExternalFactory()
252+
{
253+
$container = new ContainerBuilder();
254+
$container->register('foo', AutoconfigureWithInstanceExternalFactory::class)
255+
->setAutoconfigured(true);
256+
257+
$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithInstanceExternalFactory.php'));
258+
259+
(new RegisterAutoconfigureAttributesPass())->process($container);
260+
261+
$expected = (new ChildDefinition(''))
262+
->setFactory([new Reference('factory_for_autoconfigure'), 'createStatic'])
263+
->setBindings(['$foo' => $argument])
264+
;
265+
$this->assertEquals([AutoconfigureWithInstanceExternalFactory::class => $expected], $container->getAutoconfiguredInstanceof());
266+
}
267+
268+
public function testAutoconfigureWithInvokableFactory()
269+
{
270+
$container = new ContainerBuilder();
271+
$container->register('foo', AutoconfigureWithInvokableFactory::class)
272+
->setAutoconfigured(true);
273+
274+
$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithInvokableFactory.php'));
275+
276+
(new RegisterAutoconfigureAttributesPass())->process($container);
277+
278+
$expected = (new ChildDefinition(''))
279+
->setFactory([new Reference('factory_for_autoconfigure'), '__invoke'])
280+
->setBindings(['$foo' => $argument])
281+
;
282+
$this->assertEquals([AutoconfigureWithInvokableFactory::class => $expected], $container->getAutoconfiguredInstanceof());
283+
}
284+
285+
public function testAutoconfigureWithExpressionFactory()
286+
{
287+
$container = new ContainerBuilder();
288+
$container->register('foo', AutoconfigureWithExpressionFactory::class)
289+
->setAutoconfigured(true);
290+
291+
(new RegisterAutoconfigureAttributesPass())->process($container);
292+
293+
$expected = (new ChildDefinition(''))
294+
->setFactory('@=service("factory_for_autoconfigure").create()')
295+
;
296+
$this->assertEquals([AutoconfigureWithExpressionFactory::class => $expected], $container->getAutoconfiguredInstanceof());
297+
}
298+
211299
public function testLazyServiceAttribute()
212300
{
213301
$container = new ContainerBuilder();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Autoconfigure;
15+
16+
#[Autoconfigure(factory: '@=service("factory_for_autoconfigure").create()')]
17+
class AutoconfigureWithExpressionFactory
18+
{
19+
public function __construct(public readonly string $foo)
20+
{
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Autoconfigure;
15+
16+
#[Autoconfigure(bind: ['$foo' => 'foo'], factory: ['@factory_for_autoconfigure', 'createStatic'])]
17+
class AutoconfigureWithInstanceExternalFactory
18+
{
19+
public function __construct(public readonly string $foo)
20+
{
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Autoconfigure;
15+
16+
#[Autoconfigure(bind: ['$foo' => 'foo'], factory: '@factory_for_autoconfigure')]
17+
class AutoconfigureWithInvokableFactory
18+
{
19+
public function __construct(public readonly string $foo)
20+
{
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Autoconfigure;
15+
16+
#[Autoconfigure(bind: ['$foo' => 'foo'], factory: [FactoryDummy::class, 'create'])]
17+
class AutoconfigureWithStaticExternalFactory
18+
{
19+
public function __construct(public readonly string $foo)
20+
{
21+
}
22+
}

0 commit comments

Comments
 (0)