10000 [BC BREAK][DI] Always autowire "by id" instead of using reflection ag… · symfony/symfony@f190182 · GitHub
[go: up one dir, main page]

Skip to content

Commit f190182

Browse files
[BC BREAK][DI] Always autowire "by id" instead of using reflection against all existing services
1 parent c643776 commit f190182

31 files changed

+235
-455
lines changed

UPGRADE-3.3.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ Debug
8080
DependencyInjection
8181
-------------------
8282

83+
* [BC BREAK] autowiring now happens only when a type-hint matches its corresponding FQCN id or alias, and does not auto-register discovered type-hints as services anymore. Please follow the suggestions provided by the exceptions thrown at compilation to upgrade your service configuration.
84+
8385
* [BC BREAK] `_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names.
8486

8587
* [BC BREAK] non-numeric keys in methods and constructors arguments have never been supported and are now forbidden. Please remove them if you happen to have one.

UPGRADE-4.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ Debug
7373
DependencyInjection
7474
-------------------
7575

76+
* Autowiring now happens only when a type-hint matches its corresponding FQCN id or alias, and does not auto-register discovered type-hints as services anymore.
77+
7678
* `_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names.
7779

7880
* Non-numeric keys in methods and constructors arguments have never been supported and are now forbidden. Please remove them if you happen to have one.

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() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : false,
224+
'autowire' => $definition->isAutowired(),
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() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : 'no')
185+
."\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : '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() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : 'no');
298+
$tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : '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() ? (Definition::AUTOWIRE_BY_TYPE === $definition->getAutowired() ? 'by-type' : 'by-id') : 'false');
374+
$serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false');
375375
$serviceXML->setAttribute('file', $definition->getFile());
376376

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

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ CHANGELOG
44
3.3.0
55
-----
66

7+
* autowiring now happens only when a type-hint matches its corresponding FQCN id or alias,
8+
and does not auto-register discovered type-hints as services anymore. Please follow the suggestions
9+
provided by the exceptions thrown at compilation to upgrade your service configuration.
710
* added "ServiceSubscriberInterface" - to allow for per-class explicit service-locator definitions
811
* added "container.service_locator" tag for defining service-locator services
912
* added anonymous services support in YAML configuration files using the `!service` tag.

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

Lines changed: 49 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
1818
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
1919
use Symfony\Component\DependencyInjection\Reference;
20-
use Symfony\Component\DependencyInjection\TypedReference;
2120

2221
/**
2322
* Inspects existing service definitions and wires the autowired ones using the type hints of their classes.
@@ -30,8 +29,6 @@ class AutowirePass extends AbstractRecursivePass
3029
private $definedTypes = array();
3130
private $types;
3231
private $ambiguousServiceTypes = array();
33-
private $autowired = array();
34-
private $currentDefinition;
3532

3633
/**
3734
* {@inheritdoc}
@@ -44,7 +41,6 @@ public function process(ContainerBuilder $container)
4441
$this->definedTypes = array();
4542
$this->types = null;
4643
$this->ambiguousServiceTypes = array();
47-
$this->autowired = array();
4844
}
4945
}
5046

@@ -77,55 +73,40 @@ public static function createResourceForClass(\ReflectionClass $reflectionClass)
7773
*/
7874
protected function processValue($value, $isRoot = false)
7975
{
80-
if ($value instanceof TypedReference && $this->currentDefinition->isAutowired() && !$this->container->has((string) $value)) {
81-
if ($ref = $this->getAutowiredReference($value->getType(), $value->canBeAutoregistered())) {
82-
$value = new TypedReference((string) $ref, $value->getType(), $value->getInvalidBehavior(), $value->canBeAutoregistered());
83-
} else {
84-
$this->container->log($this, $this->createTypeNotFoundMessage($value->getType(), 'typed reference'));
85-
}
86-
}
8776
if (!$value instanceof Definition) {
8877
return parent::processValue($value, $isRoot);
8978
}
79+
if (!$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
80+
return parent::processValue($value, $isRoot);
81+
}
82+
if (!$reflectionClass = $this->container->getReflectionClass($value->getClass())) {
83+
$this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" does not exist.', $this->currentId, $value->getClass()));
9084

91-
$parentDefinition = $this->currentDefinition;
92-
$this->currentDefinition = $value;
93-
94-
try {
95-
if (!$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
96-
return parent::processValue($value, $isRoot);
97-
}
98-
if (!$reflectionClass = $this->container->getReflectionClass($value->getClass())) {
99-
$this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" does not exist.', $this->currentId, $value->getClass()));
100-
101-
return parent::processValue($value, $isRoot);
102-
}
103-
104-
$autowiredMethods = $this->getMethodsToAutowire($reflectionClass);
105-
$methodCalls = $value->getMethodCalls();
85+
return parent::processValue($value, $isRoot);
86+
}
10687

107-
if ($constructor = $this->getConstructor($value, false)) {
108-
array_unshift($methodCalls, array($constructor, $value->getArguments()));
109-
}
88+
$autowiredMethods = $this->getMethodsToAutowire($reflectionClass);
89+
$methodCalls = $value->getMethodCalls();
11090

111-
$methodCalls = $this->autowireCalls($reflectionClass, $methodCalls, $autowiredMethods);
91+
if ($constructor = $this->getConstructor($value, false)) {
92+
array_unshift($methodCalls, array($constructor, $value->getArguments()));
93+
}
11294

113-
if ($constructor) {
114-
list(, $arguments) = array_shift($methodCalls);
95+
$methodCalls = $this->autowireCalls($reflectionClass, $methodCalls, $autowiredMethods);
11596

116-
if ($arguments !== $value->getArguments()) {
117-
$value->setArguments($arguments);
118-
}
119-
}
97+
if ($constructor) {
98+
list(, $arguments) = array_shift($methodCalls);
12099

121-
if ($methodCalls !== $value->getMethodCalls()) {
122-
$value->setMethodCalls($methodCalls);
100+
if ($arguments !== $value->getArguments()) {
101+
$value->setArguments($arguments);
123102
}
103+
}
124104

125-
return parent::processValue($value, $isRoot);
126-
} finally {
127-
$this->currentDefinition = $parentDefinition;
105+
if ($methodCalls !== $value->getMethodCalls()) {
106+
$value->setMethodCalls($methodCalls);
128107
}
108+
109+
return parent::processValue($value, $isRoot);
129110
}
130111

131112
/**
@@ -186,7 +167,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, array $methodC
186167
$reflectionMethod = $autowiredMethods[$lcMethod];
187168
unset($autowiredMethods[$lcMethod]);
188169
} else {
189-
$reflectionMethod = $this->getReflectionMethod($this->currentDefinition, $method);
170+
$reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method);
190171
}
191172

192173
$arguments = $this->autowireMethod($reflectionMethod, $arguments);
@@ -242,7 +223,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
242223

243224
// no default value? Then fail
244225
if (!$parameter->isDefaultValueAvailable()) {
245-
throw new RuntimeException(sprintf('Cannot autowire service "%s": argument $%s of method %s() must have a type-hint or be given a value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
226+
throw new RuntimeException(sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" must have a type-hint or be given a value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
246227
}
247228

248229
// specifically pass the default value
@@ -251,17 +232,27 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
251232
continue;
252233
}
253234

254-
if (!$value = $this->getAutowiredReference($type)) {
255-
$failureMessage = $this->createTypeNotFoundMessage($type, sprintf('argument $%s of method %s()', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
235+
if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) {
236+
$value = new Reference($type);
237+
} else {
238+
if (null === $this->types) {
239+
$this->populateAvailableTypes();
240+
}
256241

257-
if ($parameter->isDefaultValueAvailable()) {
258-
$value = $parameter->getDefaultValue();
259-
} elseif ($parameter->allowsNull()) {
260-
$value = null;
242+
if (isset($this->definedTypes[$type])) {
243+
$value = new Reference($this->types[$type]);
261244
} else {
262-
throw new RuntimeException($failureMessage);
245+
$failureMessage = $this->createTypeNotFoundMessage($type, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
246+
247+
if ($parameter->isDefaultValueAvailable()) {
248+
$value = $parameter->getDefaultValue();
249+
} elseif ($parameter->allowsNull()) {
250+
$value = null;
251+
} else {
252+
throw new RuntimeException($failureMessage);
253+
}
254+
$this->container->log($this, $failureMessage);
263255
}
264-
$this->container->log($this, $failureMessage);
265256
}
266257

267258
$arguments[$index] = $value;
@@ -284,46 +275,6 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
284275
return $arguments;
285276
}
286277

287-
/**
288-
* @return Reference|null A reference to the service matching the given type, if any
289-
*
290-
* @throws RuntimeException
291-
*/
292-
private function getAutowiredReference($type, $autoRegister = true)
293-
{
294-
if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) {
295-
return new Reference($type);
296-
}
297-
298-
if (Definition::AUTOWIRE_BY_ID === $this->currentDefinition->getAutowired()) {
299-
return;
300-
}
301-
302-
if (isset($this->autowired[$type])) {
303-
return $this->autowired[$type] ? new Reference($this->autowired[$type]) : null;
304-
}
305-
306-
if (null === $this->types) {
307-
$this->populateAvailableTypes();
308-
}
309-
310-
if (isset($this->types[$type])) {
311-
$this->container->log($this, sprintf('Service "%s" matches type "%s" and has been autowired into service "%s".', $this->types[$type], $type, $this->currentId));
312-
313-
return new Reference($this->types[$type]);
314-
}
315-
316-
if (isset($this->ambiguousServiceTypes[$type])) {
317-
$classOrInterface = class_exists($type, false) ? 'class' : 'interface';
318-
319-
throw new RuntimeException(sprintf('Cannot autowire service "%s": multiple candidate services exist for %s "%s".%s', $this->currentId, $classOrInterface, $type, $this->createTypeAlternatives($type)));
320-
}
321-
322-
if ($autoRegister) {
323-
return $this->createAutowiredDefinition($type);
324-
}
325-
}
326-
327278
/**
328279
* Populates the list of available types.
329280
*/
@@ -344,8 +295,8 @@ private function populateAvailableTypes()
344295
*/
345296
private function populateAvailableType($id, Definition $definition)
346297
{
347-
// Never use abstract services
348-
if ($definition->isAbstract()) {
298+
// Never use abstract nor deprecated services
299+
if ($definition->isAbstract() || $definition->isDeprecated()) {
349300
return;
350301
}
351302

@@ -402,60 +353,16 @@ private function set($type, $id)
402353
$this->ambiguousServiceTypes[$type][] = $id;
403354
}
404355

405-
/**
406-
* Registers a definition for the type if possible or throws an exception.
407-
*
408-
* @param string $type
409-
*
410-
* @return Reference|null A reference to the registered definition
411-
*/
412-
private function createAutowiredDefinition($type)
413-
{
414-
if (!($typeHint = $this->container->getReflectionClass($type, true)) || !$typeHint->isInstantiable()) {
415-
return;
416-
}
417-
418-
$currentDefinition = $this->currentDefinition;
419-
$currentId = $this->currentId;
420-
$this->currentId = $this->autowired[$type] = $argumentId = sprintf('autowired.%s', $type);
421-
$this->currentDefinition = $argumentDefinition = new Definition($type);
422-
$argumentDefinition->setPublic(false);
423-
$argumentDefinition->setAutowired(true);
424-
425-
try {
426-
$this->processValue($argumentDefinition, true);
427-
$this->container->setDefinition($argumentId, $argumentDefinition);
428-
} catch (RuntimeException $e) {
429-
$this->autowired[$type] = false;
430-
$this->container->log($this, $e->getMessage());
431-
432-
return;
433-
} finally {
434-
$this->currentId = $currentId;
435-
$this->currentDefinition = $currentDefinition;
436-
}
437-
438-
$this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId));
439-
440-
return new Reference($argumentId);
441-
}
442-
443356
private function createTypeNotFoundMessage($type, $label)
444357
{
445-
$autowireById = Definition::AUTOWIRE_BY_ID === $this->currentDefinition->getAutowired();
446-
if (!$classOrInterface = class_exists($type, $autowireById) ? 'class' : (interface_exists($type, false) ? 'interface' : null)) {
447-
return sprintf('Cannot autowire service "%s": %s has type "%s" but this class does not exist.', $this->currentId, $label, $type);
448-
}
449-
if (null === $this->types) {
450-
$this->populateAvailableTypes();
451-
}
452-
if ($autowireById) {
453-
$message = sprintf('%s references %s "%s" but no such service exists.%s', $label, $classOrInterface, $type, $this->createTypeAlternatives($type));
358+
if (!$r = $this->container->getReflectionClass($type, true)) {
359+
$message = sprintf('has type "%s" but this class does not exist.', $type);
454360
} else {
455-
$message = sprintf('no services were found matching the "%s" %s and it cannot be auto-registered for %s.', $type, $classOrInterface, $label);
361+
$message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';
362+
$message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $this->createTypeAlternatives($type));
456363
}
457364

458-
return sprintf('Cannot autowire service "%s": %s', $this->currentId, $message);
365+
return sprintf('Cannot autowire service "%s": %s %s', $this->currentId, $label, $message);
459366
}
460367

461368
private function createTypeAlternatives($type)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ protected function processValue($value, $isRoot = false)
9595
}
9696

9797
$serviceLocator = $this->serviceLocator;
98-
$this->serviceLocator = (string) ServiceLocatorTagPass::register($this->container, $subscriberMap, $value->getAutowired());
98+
$this->serviceLocator = (string) ServiceLocatorTagPass::register($this->container, $subscriberMap);
9999

100100
try {
101101
return parent::processValue($value);

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

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

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

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

0 commit comments

Comments
 (0)
0