10000 [DI] Add side-effect-restricted variant for autowiring, based solely … · symfony/symfony@36d76e9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 36d76e9

Browse files
[DI] Add side-effect-restricted variant for autowiring, based solely on ids
1 parent 6643fe2 commit 36d76e9

File tree

17 files changed

+140
-54
lines changed

17 files changed

+140
-54
lines changed

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ private function getContainerDefinitionData(Definition $definition, $omitTags =
221221
'lazy' => $definition->isLazy(),
222222
'shared' => $definition->isShared(),
223223
'abstract' => $definition->isAbstract(),
224-
'autowire' => $definition->isAutowired(),
224+
'autowire' => $definition->isAutowired() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : false,
225225
);
226226

227227
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ protected function describeContainerDefinition(Definition $definition, array $op
182182
."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no')
183183
."\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no')
184184
."\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no')
185-
."\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no')
185+
."\n".'- Autowired: '.($definition->isAutowired() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : 'no')
186186
;
187187

188188
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ protected function describeContainerDefinition(Definition $definition, array $op
295295
$tableRows[] = array('Lazy', $definition->isLazy() ? 'yes' : 'no');
296296
$tableRows[] = array('Shared', $definition->isShared() ? 'yes' : 'no');
297297
$tableRows[] = array('Abstract', $definition->isAbstract() ? 'yes' : 'no');
298-
$tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : 'no');
298+
$tableRows[] = array('Autowired', $definition->isAutowired() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : 'no');
299299

300300
if ($autowiringTypes = $definition->getAutowiringTypes(false)) {
301301
$tableRows[] = array('Autowiring Types', implode(', ', $autowiringTypes));

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ private function getContainerDefinitionDocument(Definition $definition, $id = nu
371371
$serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false');
372372
$serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false');
373373
$serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false');
374-
$serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false');
374+
$serviceXML->setAttribute('autowired', $definition->isAutowired() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : 'false');
375375
$serviceXML->setAttribute('file', $definition->getFile());
376376

377377
$calls = $definition->getMethodCalls();

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

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
use Symfony\Component\DependencyInjection\TypedReference;
2121

2222
/**
23-
* Guesses constructor arguments of services definitions and try to instantiate services if necessary.
23+
* Inspects existing service definitions and wires the autowired ones using the type hints of their classes.
2424
*
2525
* @author Kévin Dunglas <dunglas@gmail.com>
26+
* @author Nicolas Grekas <p@tchwork.com>
2627
*/
2728
class AutowirePass extends AbstractRecursivePass
2829
{
@@ -280,13 +281,7 @@ private function autowireMethod(\ReflectionMethod $reflectionMethod, array $argu
280281
} elseif ($parameter->allowsNull()) {
281282
$value = null;
282283
} else {
283-
if ($classOrInterface = class_exists($type, false) ? 'class' : (interface_exists($type, false) ? 'interface' : null)) {
284-
$message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $type, $this->currentId, $classOrInterface);
285-
} else {
286-
$message = sprintf('Cannot autowire argument $%s of method %s::%s() for service "%s": Class %s does not exist.', $parameter->name, $reflectionMethod->class, $reflectionMethod->name, $this->currentId, $type);
287-
}
288-
289-
throw new RuntimeException($message);
284+
throw $this->createTypeNotFoundException($type, 'argument $'.$parameter->name.' of method '.$reflectionMethod->class.'::'.$reflectionMethod->name.'()');
290285
}
291286

292287
$arguments[$index] = $value;
@@ -339,17 +334,23 @@ private function getAutowiredReference($type, $autoRegister = true)
339334
return new Reference($type);
340335
}
341336

342-
if (null === $this->types) {
343-
$this->populateAvailableTypes();
344-
}
337+
if (Definition::AUTOWIRE_BY_TYPE === $this->currentDefinition->getAutowired()) {
338+
if (null === $this->types) {
339+
$this->populateAvailableTypes();
340+
}
341+
342+
if (isset($this->types[$type])) {
343+
$this->container->log($this, sprintf('Service "%s" matches type "%s" thus has been autowired into service "%s".', $this->types[$type], $type, $this->currentId));
345344

346-
if (isset($this->types[$type])) {
347-
return new Reference($this->types[$type]);
345+
return new Reference($this->types[$type]);
346+
}
348347
}
349348

350349
if ($autoRegister && $class = $this->container->getReflectionClass($type, true)) {
351350
return $this->createAutowiredDefinition($class);
352351
}
352+
353+
$this->container->log($this, sprintf('Type "%s" cannot be autowired for service "%s".', $type, $this->currentId));
353354
}
354355

355356
/**
@@ -449,22 +450,57 @@ private function createAutowiredDefinition(\ReflectionClass $typeHint)
449450
}
450451

451452
if (!$typeHint->isInstantiable()) {
453+
$this->container->log($this, sprintf('Type "%s" is not instantiable thus cannot be auto-registered for service "%s".', $typeHint->name, $this->currentId));
454+
452455
return;
453456
}
454457

455458
$currentId = $this->currentId;
456-
$this->currentId = $argumentId = sprintf('autowired.%s', $typeHint->name);
459+
$this->currentId = $type = $typeHint->name;
457460

458-
$argumentDefinition = $this->container->register($argumentId, $typeHint->name);
459-
$argumentDefinition->setPublic(false);
460-
$argumentDefinition->setAutowired(true);
461+
$typeDefinition = (new Definition($typeHint->name))
462+
->setPublic(false)
463+
->setAutowired($this->currentDefinition->getAutowired())
464+
->addTag('container.autoregistered');
461465

462-
$this->populateAvailableType($argumentId, $argumentDefinition);
466+
if (null !== $this->types) {
467+
$this->populateAvailableType($type, $typeDefinition);
468+
}
469+
470+
try {
471+
$this->processValue($typeDefinition, true);
472+
$this->container->setDefinition($type, $typeDefinition);
473+
} catch (RuntimeException $e) {
474+
$this->container->log($this, sprintf('Type "%s" cannot be auto-registered for service "%s": %s.', $type, $this->currentId, $e->getMessage()));
463475

464-
$this->processValue($argumentDefinition, true);
465-
$this->currentId = $currentId;
476+
return;
477+
} finally {
478+
$this->currentId = $currentId;
479+
}
480+
$this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId));
481+
482+
return new Reference($type);
483+
}
484+
485+
private function createTypeNotFoundException($type, $label)
486+
{
487+
if (!$classOrInterface = class_exists($type, false) ? 'class' : (interface_exists($type, false) ? 'interface' : null)) {
488+
return new RuntimeException(sprintf('Cannot autowire %s for service "%s": Class or interface "%s" does not exist.', $label, $this->currentId, $type));
489+
}
490+
if (null === $this->types) {
491+
$this->populateAvailableTypes();
492+
}
493+
if (Definition::AUTOWIRE_BY_TYPE === $this->currentDefinition->getAutowired()) {
494+
$message = sprintf('No services were found matching the "%s" %s and it cannot be auto-registered', $type, $classOrInterface);
495+
} elseif (isset($this->ambiguousServiceTypes[$type])) {
496+
$message = sprintf('Service "%s" does not exist and it cannot be auto-registered. This type could be aliased to any of these existing services: "%s"', $type, implode('", "', $this->ambiguousServiceTypes[$type]));
497+
} elseif (isset($this->types[$type])) {
498+
$message = sprintf('Service "%s" does not exist and it cannot be auto-registered. This type could be aliased to the existing "%s" service', $type, $this->types[$type]);
499+
} else {
500+
$message = sprintf('Service "%s" does not exist and it cannot be auto-registered', $type);
501+
}
466502

467-
return new Reference($argumentId);
503+
return new RuntimeException(sprintf('Cannot autowire %s for service "%s": %s.', $label, $this->currentId, $message));
468504
}
469505

470506
/**

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private function doResolveDefinition(ChildDefinition $definition)
101101
$def->setFile($parentDef->getFile());
102102
$def->setPublic($parentDef->isPublic());
103103
$def->setLazy($parentDef->isLazy());
104-
$def->setAutowired($parentDef->isAutowired());
104+
$def->setAutowired($parentDef->getAutowired());
105105

106106
self::mergeDefinition($def, $definition);
107107

@@ -147,7 +147,7 @@ public static function mergeDefinition(Definition $def, ChildDefinition $definit
147147
$def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%'));
148148
}
149149
if (isset($changes['autowired'])) {
150-
$def->setAutowired($definition->isAutowired());
150+
$def->setAutowired($definition->getAutowired());
151151
}
152152
if (isset($changes['decorated_service'])) {
153153
$decoratedService = $definition->getDecoratedService();

src/Symfony/Component/DependencyInjection/Definition.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
*/
2222
class Definition
2323
{
24+
const AUTOWIRE_BY_TYPE = 1;
25+
const AUTOWIRE_BY_ID = 2;
26+
2427
private $class;
2528
private $file;
2629
private $factory;
@@ -38,7 +41,7 @@ class Definition
3841
private $abstract = false;
3942
private $lazy = false;
4043
private $decoratedService;
41-
private $autowired = false;
44+
private $autowired = 0;
4245
private $autowiringTypes = array();
4346

4447
protected $arguments;
@@ -736,20 +739,35 @@ public function setAutowiringTypes(array $types)
736739
* @return bool
737740
*/
738741
public function isAutowired()
742+
{
743+
return (bool) $this->autowired;
744+
}
745+
746+
/**
747+
* Gets the autowiring mode.
748+
*
749+
* @return int
750+
*/
751+
public function getAutowired()
739752
{
740753
return $this->autowired;
741754
}
742755

743756
/**
744757
* Sets autowired.
745758
*
746-
* @param bool $autowired
759+
* @param bool|int $autowired
747760
*
748761
* @return $this
749762
*/
750763
public function setAutowired($autowired)
751764
{
752-
$this->autowired = (bool) $autowired;
765+
$autowired = (int) $autowired;
766+
767+
if ($autowired && self::AUTOWIRE_BY_TYPE !== $autowired && self::AUTOWIRE_BY_ID !== $autowired) {
768+
throw new InvalidArgumentException(sprintf('Invalid argument: Definition::AUTOWIRE_BY_TYPE (%d) or Definition::AUTOWIRE_BY_ID (%d) expected, %d given.', self::AUTOWIRE_BY_TYPE, self::AUTOWIRE_BY_ID, $autowired));
769+
}
770+
$this->autowired = $autowired;
753771

754772
return $this;
755773
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,10 +734,11 @@ private function addService($id, Definition $definition)
734734
}
735735

736736
if ($definition->isAutowired()) {
737+
$autowired = Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'types' : 'ids';
737738
$doc .= <<<EOF
738739
739740
*
740-
* This service is autowired.
741+
* This service is autowired by {$autowired}.
741742
EOF;
742743
}
743744

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ private function addService($definition, $id, \DOMElement $parent)
199199
}
200200

201201
if ($definition->isAutowired()) {
202-
$service->setAttribute('autowire', 'true');
202+
$service->setAttribute('autowire', Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id');
203203
}
204204

205205
foreach ($definition->getAutowiringTypes(false) as $autowiringTypeValue) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ private function addService($id, $definition)
106106
}
107107

108108
if ($definition->isAutowired()) {
109-
$code .= " autowire: true\n";
109+
$code .= sprintf(" autowire: %s\n", Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by_type' : 'by_id');
110110
}
111111

112112
$autowiringTypesCode = '';

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ private function getServiceDefaults(\DOMDocument $xml, $file)
172172
}
173173
}
174174
if ($defaultsNode->hasAttribute('autowire')) {
175-
$defaults['autowire'] = XmlUtils::phpize($defaultsNode->getAttribute('autowire'));
175+
$defaults['autowire'] = $this->getAutowired($defaultsNode->getAttribute('autowire'), $file);
176176
}
177177
if ($defaultsNode->hasAttribute('public')) {
178178
$defaults['public'] = XmlUtils::phpize($defaultsNode->getAttribute('public'));
@@ -238,7 +238,7 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults =
238238
}
239239

240240
if ($value = $service->getAttribute('autowire')) {
241-
$definition->setAutowired(XmlUtils::phpize($value));
241+
$definition->setAutowired($this->getAutowired($value, $file));
242242
} elseif (isset($defaults['autowire'])) {
243243
$definition->setAutowired($defaults['autowire']);
244244
}
@@ -666,6 +666,23 @@ private function loadFromExtensions(\DOMDocument $xml)
666666
}
667667
}
668668

669+
private function getAutowired($value, $file)
670+
{
671+
if (is_bool($value = XmlUtils::phpize($value))) {
672+
return $value;
673+
}
674+
675+
if ('by-type' === $value) {
676+
return Definition::AUTOWIRE_BY_TYPE;
677+
}
678+
679+
if ('by-id' === $value) {
680+
return Definition::AUTOWIRE_BY_ID;
681+
}
682+
683+
throw new InvalidArgumentException(sprintf('Invalid autowire attribute: "by-type", "by-id", "true" or "false" expected, "%s" given in "%s".', $value, $file));
684+
}
685+
669686
/**
670687
* Converts a \DomElement object to a PHP array.
671688
*

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,14 @@ private function parseDefinition($id, $service, $file, array $defaults)
500500

501501
$autowire = isset($service['autowire']) ? $service['autowire'] : (isset($defaults['autowire']) ? $defaults['autowire'] : null);
502502
if (null !== $autowire) {
503+
if ('by_type' === $autowire) {
504+
$autowire = Definition::AUTOWIRE_BY_TYPE;
505+
} elseif ('by_id' === $autowire) {
506+
$autowire = Definition::AUTOWIRE_BY_ID;
507+
} elseif (!is_bool($autowire)) {
508+
throw new InvalidArgumentException(sprintf('Invalid autowire attribute: "by_type", "by_id", true or false expected, "%s" given in "%s".', is_string($autowire) ? $autowire : gettype($autowire), $file));
509+
}
510+
503511
$definition->setAutowired($autowire);
504512
}
505513

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
103103
</xsd:choice>
104104
<xsd:attribute name="public" type="boolean" />
105-
<xsd:attribute name="autowire" type="boolean" />
105+
<xsd:attribute name="autowire" type="autowire" />
106106
<xsd:attribute name="inherit-tags" type="boolean" />
107107
</xsd:complexType>
108108

@@ -131,7 +131,7 @@
131131
<xsd:attribute name="decorates" type="xsd:string" />
132132
<xsd:attribute name="decoration-inner-name" type="xsd:string" />
133133
<xsd:attribute name="decoration-priority" type="xsd:integer" />
134-
<xsd:attribute name="autowire" type="boolean" />
134+
<xsd:attribute name="autowire" type="autowire" />
135135
<xsd:attribute name="inherit-tags" type="boolean" />
136136
</xsd:complexType>
137137

@@ -151,7 +151,7 @@
151151
<xsd:attribute name="public" type="boolean" />
152152
<xsd:attribute name="lazy" type="boolean" />
153153
<xsd:attribute name="abstract" type="boolean" />
154-
<xsd:attribute name="autowire" type="boolean" />
154+
<xsd:attribute name="autowire" type="autowire" />
155155
</xsd:complexType>
156156

157157
<xsd:complexType name="prototype">
@@ -172,7 +172,7 @@
172172
<xsd:attribute name="lazy" type="boolean" />
173173
<xsd:attribute name="abstract" type="boolean" />
174174
<xsd:attribute name="parent" type="xsd:string" />
175-
<xsd:attribute name="autowire" type="boolean" />
175+
<xsd:attribute name="autowire" type="autowire" />
176176
<xsd:attribute name="inherit-tags" type="boolean" />
177177
</xsd:complexType>
178178

@@ -279,4 +279,10 @@
279279
<xsd:pattern value="(%.+%|true|false)" />
280280
</xsd:restriction>
281281
</xsd:simpleType>
282+
283+
<xsd:simpleType name="autowire">
284+
<xsd:restriction base="xsd:string">
285+
<xsd:pattern value="(true|false|by-type|by-id)" />
286+
</xsd:restriction>
287+
</xsd:simpleType>
282288
</xsd:schema>

0 commit comments

Comments
 (0)
0