Skip to content

Commit 9de21bb

Browse files
committed
Allow using expressions as service factories
1 parent abd7a2c commit 9de21bb

21 files changed

+155
-9
lines changed

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
* Add `$exclude` to `TaggedIterator` and `TaggedLocator` attributes
88
* Add `$exclude` to `tagged_iterator` and `tagged_locator` configurator
9+
* Add support for using expressions as service factories
910

1011
6.0
1112
---

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,10 @@ private function createService(Definition $definition, array &$inlineServices, b
10131013
if (null !== $factory = $definition->getFactory()) {
10141014
if (\is_array($factory)) {
10151015
$factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]];
1016+
} elseif ($factory instanceof Expression) {
1017+
$factory = function () use ($factory) {
1018+
return $this->getExpressionLanguage()->evaluate($factory, ['container' => $this]);
1019+
};
10161020
} elseif (!\is_string($factory)) {
10171021
throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory.', $id));
10181022
}

src/Symfony/Component/DependencyInjection/Definition.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
1515
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1616
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
17+
use Symfony\Component\ExpressionLanguage\Expression;
1718

1819
/**
1920
* Definition represents a service definition.
@@ -26,7 +27,7 @@ class Definition
2627

2728
private ?string $class = null;
2829
private ?string $file = null;
29-
private string|array|null $factory = null;
30+
private string|array|Expression|null $factory = null;
3031
private bool $shared = true;
3132
private array $deprecation = [];
3233
private array $properties = [];
@@ -94,11 +95,11 @@ public function setChanges(array $changes): static
9495
/**
9596
* Sets a factory.
9697
*
97-
* @param string|array|Reference|null $factory A PHP function, reference or an array containing a class/Reference and a method to call
98+
* @param string|array|Expression|Reference|null $factory A PHP function, reference or an array containing a class/Reference and a method to call
9899
*
99100
* @return $this
100101
*/
101-
public function setFactory(string|array|Reference|null $factory): static
102+
public function setFactory(string|array|Expression|Reference|null $factory): static
102103
{
103104
$this->changes['factory'] = true;
104105

@@ -116,9 +117,9 @@ public function setFactory(string|array|Reference|null $factory): static
116117
/**
117118
* Gets the factory.
118119
*
119-
* @return string|array|null The PHP function or an array containing a class/Reference and a method to call
120+
* @return string|array|Expression|null The PHP function or an array containing a class/Reference and a method to call
120121
*/
121-
public function getFactory(): string|array|null
122+
public function getFactory(): string|array|Expression|null
122123
{
123124
return $this->factory;
124125
}

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,10 @@ private function addNewInstance(Definition $definition, string $return = '', str
11591159
return $return.sprintf("[%s, '%s'](%s)", $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail;
11601160
}
11611161

1162+
if ($callable instanceof Expression) {
1163+
return $return.$this->getExpressionLanguage()->compile((string) $callable, ['this' => 'container']).$tail;
1164+
}
1165+
11621166
return $return.sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '').$tail;
11631167
}
11641168

src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ private function addService(Definition $definition, ?string $id, \DOMElement $pa
171171
$factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
172172
}
173173
$factory->setAttribute('method', $callable[1]);
174+
} elseif ($callable instanceof Expression) {
175+
$factory->setAttribute('expression', $callable);
174176
} else {
175177
$factory->setAttribute('function', $callable);
176178
}

src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ private function addParameters(): string
224224
*/
225225
private function dumpCallable(mixed $callable): mixed
226226
{
227+
if ($callable instanceof Expression) {
228+
return $this->getExpressionCall((string) $callable);
229+
}
230+
227231
if (\is_array($callable)) {
228232
if ($callable[0] instanceof Reference) {
229233
$callable = [$this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]];

src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/FactoryTrait.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1515
use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator;
16+
use Symfony\Component\ExpressionLanguage\Expression;
1617

1718
trait FactoryTrait
1819
{
@@ -21,7 +22,7 @@ trait FactoryTrait
2122
*
2223
* @return $this
2324
*/
24-
final public function factory(string|array|ReferenceConfigurator $factory): static
25+
final public function factory(string|array|Expression|ReferenceConfigurator $factory): static
2526
{
2627
if (\is_string($factory) && 1 === substr_count($factory, ':')) {
2728
$factoryParts = explode(':', $factory);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ private function parseDefinition(\DOMElement $service, string $file, Definition
304304
$factory = $factories[0];
305305
if ($function = $factory->getAttribute('function')) {
306306
$definition->setFactory($function);
307+
} elseif ($expression = $factory->getAttribute('expression')) {
308+
$definition->setFactory(new Expression($expression));
307309
} else {
308310
if ($childService = $factory->getAttribute('service')) {
309311
$class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,9 +701,13 @@ private function parseDefinition(string $id, array|string|null $service, string
701701
/**
702702
* @throws InvalidArgumentException When errors occur
703703
*/
704-
private function parseCallable(mixed $callable, string $parameter, string $id, string $file): string|array|Reference
704+
private function parseCallable(mixed $callable, string $parameter, string $id, string $file): string|array|Expression|Reference
705705
{
706706
if (\is_string($callable)) {
707+
if ('factory' === $parameter && str_starts_with($callable, '@=')) {
708+
return $this->resolveServices($callable, $file);
709+
}
710+
707711
if ('' !== $callable && '@' === $callable[0]) {
708712
if (!str_contains($callable, ':')) {
709713
return [$this->resolveServices($callable, $file), '__invoke'];

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,17 @@
115115
<xsd:attribute name="function" type="xsd:string" />
116116
</xsd:complexType>
117117

118+
<xsd:complexType name="factory">
119+
<xsd:choice minOccurs="0" maxOccurs="1">
120+
<xsd:element name="service" type="service" minOccurs="0" maxOccurs="1" />
121+
</xsd:choice>
122+
<xsd:attribute name="service" type="xsd:string" />
123+
<xsd:attribute name="class" type="xsd:string" />
124+
<xsd:attribute name="method" type="xsd:string" />
125+
<xsd:attribute name="function" type="xsd:string" />
126+
<xsd:attribute name="expression" type="xsd:string" />
127+
</xsd:complexType>
128+
118129
<xsd:complexType name="defaults">
119130
<xsd:annotation>
120131
<xsd:documentation><![CDATA[
@@ -135,7 +146,7 @@
135146
<xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" />
136147
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
137148
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
138-
<xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" />
149+
<xsd:element name="factory" type="factory" minOccurs="0" maxOccurs="1" />
139150
<xsd:element name="deprecated" type="deprecated" minOccurs="0" maxOccurs="1" />
140151
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
141152
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
@@ -179,7 +190,7 @@
179190
<xsd:choice maxOccurs="unbounded">
180191
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
181192
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
182-
<xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" />
193+
<xsd:element name="factory" type="factory" minOccurs="0" maxOccurs="1" />
183194
<xsd:element name="deprecated" type="deprecated" minOccurs="0" maxOccurs="1" />
184195
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
185196
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />

0 commit comments

Comments
 (0)