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

Skip to content

[DependencyInjection] add factory argument in the #[Autoconfigure] attribute and also enabled the use of factory within instanceof conditionals #60589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Autoconfigure
* @param array<string, mixed>|null $properties The properties to define when creating the service
* @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
* @param string|null $constructor The public static method to use to instantiate the service
* @param array<string|null, string|null>|string|null $factory The factory that defines how to create the service
*/
public function __construct(
public ?array $tags = null,
Expand All @@ -42,6 +43,7 @@ public function __construct(
public ?array $properties = null,
public array|string|null $configurator = null,
public ?string $constructor = null,
public array|string|null $factory = null,
) {
}
}
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CHANGELOG
---

* Allow `#[AsAlias]` to be extended
* 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

7.3
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class InstanceofConfigurator extends AbstractServiceConfigurator
use Traits\CallTrait;
use Traits\ConfiguratorTrait;
use Traits\ConstructorTrait;
use Traits\FactoryTrait;
use Traits\LazyTrait;
use Traits\PropertyTrait;
use Traits\PublicTrait;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class YamlFileLoader extends FileLoader
'autowire' => 'autowire',
'bind' => 'bind',
'constructor' => 'constructor',
'factory' => 'factory',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this change the YamlFileLoader when the PR description talks only about attributes ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code internally uses the YamlFileLoader to read values found in attributes

this makes me wonder about the yaml + xml + php config formats: they should also support this factory entry

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we should indeed support it in all formats, and make the changelog entry (and the PR title and description) mention that explicitly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and we should also have tests preventing regressions for those other formats.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @stof @nicolas-grekas 👋 I hope you're both doing well! 😊
I've made the requested changes, please let me know if there's anything else you'd like me to adjust. 😊

Thanks! ❤️

];

private const DEFAULTS_KEYWORDS = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
<xsd:complexType name="instanceof">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="factory" type="factory" minOccurs="0" maxOccurs="1" />
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedOverwrite;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedProperties;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedTag;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithExpressionFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithInstanceExternalFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithInvokableFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithStaticExternalFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureWithStaticSelfFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\LazyAutoconfigured;
use Symfony\Component\DependencyInjection\Tests\Fixtures\LazyLoaded;
use Symfony\Component\DependencyInjection\Tests\Fixtures\MultipleAutoconfigureAttributed;
Expand Down Expand Up @@ -208,6 +214,88 @@ public function testStaticConstructor()
$this->assertEquals([StaticConstructorAutoconfigure::class => $expected], $container->getAutoconfiguredInstanceof());
}

public function testAutoconfigureWithStaticSelfFactory()
{
$container = new ContainerBuilder();
$container->register('foo', AutoconfigureWithStaticSelfFactory::class)
->setAutoconfigured(true);

$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithStaticSelfFactory.php'));

(new RegisterAutoconfigureAttributesPass())->process($container);

$expected = (new ChildDefinition(''))
->setFactory([null, 'create'])
->setBindings(['$foo' => $argument])
;
$this->assertEquals([AutoconfigureWithStaticSelfFactory::class => $expected], $container->getAutoconfiguredInstanceof());
}

public function testAutoconfigureWithStaticExternalFactory()
{
$container = new ContainerBuilder();
$container->register('foo', AutoconfigureWithStaticExternalFactory::class)
->setAutoconfigured(true);

$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithStaticExternalFactory.php'));

(new RegisterAutoconfigureAttributesPass())->process($container);

$expected = (new ChildDefinition(''))
->setFactory([FactoryDummy::class, 'create'])
->setBindings(['$foo' => $argument])
;
$this->assertEquals([AutoconfigureWithStaticExternalFactory::class => $expected], $container->getAutoconfiguredInstanceof());
}

public function testAutoconfigureWithInstanceExternalFactory()
{
$container = new ContainerBuilder();
$container->register('foo', AutoconfigureWithInstanceExternalFactory::class)
->setAutoconfigured(true);

$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithInstanceExternalFactory.php'));

(new RegisterAutoconfigureAttributesPass())->process($container);

$expected = (new ChildDefinition(''))
->setFactory([new Reference('factory_for_autoconfigure'), 'createStatic'])
->setBindings(['$foo' => $argument])
;
$this->assertEquals([AutoconfigureWithInstanceExternalFactory::class => $expected], $container->getAutoconfiguredInstanceof());
}

public function testAutoconfigureWithInvokableFactory()
{
$container = new ContainerBuilder();
$container->register('foo', AutoconfigureWithInvokableFactory::class)
->setAutoconfigured(true);

$argument = new BoundArgument('foo', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureWithInvokableFactory.php'));

(new RegisterAutoconfigureAttributesPass())->process($container);

$expected = (new ChildDefinition(''))
->setFactory([new Reference('factory_for_autoconfigure'), '__invoke'])
->setBindings(['$foo' => $argument])
;
$this->assertEquals([AutoconfigureWithInvokableFactory::class => $expected], $container->getAutoconfiguredInstanceof());
}

public function testAutoconfigureWithExpressionFactory()
{
$container = new ContainerBuilder();
$container->register('foo', AutoconfigureWithExpressionFactory::class)
->setAutoconfigured(true);

(new RegisterAutoconfigureAttributesPass())->process($container);

$expected = (new ChildDefinition(''))
->setFactory('@=service("factory_for_autoconfigure").create()')
;
$this->assertEquals([AutoconfigureWithExpressionFactory::class => $expected], $container->getAutoconfiguredInstanceof());
}

public function testLazyServiceAttribute()
{
$container = new ContainerBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(factory: '@=service("factory_for_autoconfigure").create()')]
class AutoconfigureWithExpressionFactory
{
public function __construct(public readonly string $foo)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(bind: ['$foo' => 'foo'], factory: ['@factory_for_autoconfigure', 'createStatic'])]
class AutoconfigureWithInstanceExternalFactory
{
public function __construct(public readonly string $foo)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(bind: ['$foo' => 'foo'], factory: '@factory_for_autoconfigure')]
class AutoconfigureWithInvokableFactory
{
public function __construct(public readonly string $foo)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(bind: ['$foo' => 'foo'], factory: [FactoryDummy::class, 'create'])]
class AutoconfigureWithStaticExternalFactory
{
public function __construct(public readonly string $foo)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(bind: ['$foo' => 'foo'], factory: [null, 'create'])]
class AutoconfigureWithStaticSelfFactory
{
public function __construct(public readonly string $foo)
{
}

public static function create(string $foo): static
{
return new self($foo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

services:
service_container:
class: Symfony\Component\DependencyInjection\ContainerInterface
public: true
synthetic: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Bar:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar
public: true
tags:
- bar
factory: ['@Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory', getDefaultBar]
Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory
public: true
arguments: [!tagged_iterator bar]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar;
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface;

return function (ContainerConfigurator $container) {
$services = $container->services()->defaults()->public();

$services->instanceof(BarInterface::class)
->factory([new Reference(BarFactory::class), 'getDefaultBar'])
->tag('bar');

$services->set(Bar::class)
->public();

$services->set(BarFactory::class)
->args([new TaggedIteratorArgument('bar')]);
};
Original file line number Diff line number Diff line change
@@ -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>
<instanceof id="Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface" lazy="true" autowire="true">
<factory service="Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory" method="getDefaultBar"/>
<tag name="foo" />
<tag name="bar"/>
</instanceof>

<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\Bar" public="true"/>

<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory">
<argument type="tagged_iterator" tag="bar"/>
</service>
</services>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
_instanceof:
Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface:
factory: ['@Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory', 'getDefaultBar']
tags:
- { name: bar }

Symfony\Component\DependencyInjection\Tests\Fixtures\Bar:
public: true

Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory:
arguments: [!tagged_iterator 'bar']
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public static function provideConfig()
yield ['static_constructor'];
yield ['inline_static_constructor'];
yield ['instanceof_static_constructor'];
yield ['instanceof_factory'];
yield ['closure'];
yield ['from_callable'];
yield ['env_param'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -941,11 +941,14 @@ public function testNamedArguments()
$this->assertEquals([['setApiKey', ['123']]], $container->getDefinition(NamedArgumentsDummy::class)->getMethodCalls());
}

public function testInstanceof()
/**
* @dataProvider provideServiceInstanceOfFactoryFiles
*/
public function testInstanceof(string $fileName)
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('services_instanceof.xml');
$loader->load($fileName);
$container->compile();

$definition = $container->getDefinition(Bar::class);
Expand All @@ -954,6 +957,14 @@ public function testInstanceof()
$this->assertSame(['foo' => [[]], 'bar' => [[]]], $definition->getTags());
}

public static function provideServiceInstanceOfFactoryFiles(): iterable
{
return [
['services_instanceof.xml'],
['services_instanceof_factory.xml'],
];
}

public function testEnumeration()
{
$container = new ContainerBuilder();
Expand Down
Loading