You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feature #22295 [BC BREAK][DI] Always autowire "by id" instead of using reflection against all existing services (nicolas-grekas)
This PR was merged into the 3.3-dev branch.
Discussion
----------
[BC BREAK][DI] Always autowire "by id" instead of using reflection against all existing services
| Q | A
| ------------- | ---
| Branch? | 3.3
| Bug fix? | no
| New feature? | yes
| BC breaks? | yes - compile time only
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
(patch best reviewed [ignoring whitespaces](https://github.com/symfony/symfony/pull/22295/files?w=1).)
"By-id" autowiring, as introduced in #22060 is free from all the issues that "by-type" autowiring has:
- it has no magic and requires explicit type<>id matching (*vs* using reflection on all services to cherry-pick *the* one that matches some type-hint *at that time*, which is fragile)
- it is free from any ambiguities (*vs* the Damocles' sword of breaking config just by enabling some unrelated bundle)
- it is easily introspected: just look at DI config files (*vs* inspecting the type-hierarchy of all services + their type-hints)
- ~~it is side-effect free, thus plain predictable (*vs* auto-registration of discovered types as services)~~
- it plays nice with deprecated services or classes (see #22282)
- *etc.*
Another consideration is that any "by-type" autowired configuration is either broken (because of future ambiguities) - or equivalent to a "by-id" configuration (because resolving ambiguities *means* adding explicit type<>id mappings.) For theoreticians, we could say that "by-id" autowiring is the asymptotic limit of "by-type" autowiring :-)
For all these reasons - and also because it reduces the complexity of the code base - I propose to change the behavior and only support "by-id" autowiring in 3.3. This will break some configurations relying on "by-type" autowiring. Yet the break will only happen at compile-time, which means this won't *silently* break any apps. For all core Symfony services, they will work out of the box thanks to #22098 *et al.* For the remaining services, fixing ones config should be pretty straightforward: just follow the suggestions provided by the exception messages. If they are fine to you, you'll end up with the exact same config in the end. And maybe you'll spot issues that were hidden previously.
Commits
-------
cc5e582 [BC BREAK][DI] Always autowire "by id" instead of using reflection against all existing services
Copy file name to clipboardExpand all lines: UPGRADE-3.3.md
+2
Original file line number
Diff line number
Diff line change
@@ -80,6 +80,8 @@ Debug
80
80
DependencyInjection
81
81
-------------------
82
82
83
+
*[BC BREAK] autowiring now happens only when a type-hint matches its corresponding FQCN id or alias. Please follow the suggestions provided by the exceptions thrown at compilation to upgrade your service configuration.
84
+
83
85
*[BC BREAK]`_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names.
84
86
85
87
*[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.
Copy file name to clipboardExpand all lines: UPGRADE-4.0.md
+2
Original file line number
Diff line number
Diff line change
@@ -73,6 +73,8 @@ Debug
73
73
DependencyInjection
74
74
-------------------
75
75
76
+
* Autowiring now happens only when a type-hint matches its corresponding FQCN id or alias.
77
+
76
78
*`_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names.
77
79
78
80
* 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.
@@ -242,7 +233,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
242
233
243
234
// no default value? Then fail
244
235
if (!$parameter->isDefaultValueAvailable()) {
245
-
thrownewRuntimeException(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));
236
+
thrownewRuntimeException(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));
246
237
}
247
238
248
239
// specifically pass the default value
@@ -251,8 +242,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
251
242
continue;
252
243
}
253
244
254
-
if (!$value = $this->getAutowiredReference($type)) {
@@ -307,19 +292,11 @@ private function getAutowiredReference($type, $autoRegister = true)
307
292
$this->populateAvailableTypes();
308
293
}
309
294
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));
@@ -82,6 +84,9 @@ protected function processValue($value, $isRoot = false)
82
84
$key = $type;
83
85
}
84
86
if (!isset($serviceMap[$key])) {
87
+
if (!$autowire) {
88
+
thrownewInvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by %s::getSubscribedServices().', $this->currentId, $key, $class));
89
+
}
85
90
$serviceMap[$key] = newReference($type);
86
91
}
87
92
@@ -95,7 +100,7 @@ protected function processValue($value, $isRoot = false)
0 commit comments