-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DependencyInjection] Introduce a flag to enable setter autowiring #19631
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
Conversation
Looks good to me! 👍 |
What about allowing a collection of setters to autowire; |
@ro0NL I don't think there is much benefit compared to the existing calls:
- [setMailer, ['@app.mailer']] |
To clarify on why this change is needed, enabling auto-wiring without this flag could cause a BC break. Given following example service: class Foo
{
private $baz;
public function __construct(Bar $bar)
{
$this->baz = $bar;
}
public function setBaz(Taz $taz)
{
$this->baz = $taz;
}
} Turning auto-wiring on in earlier Current This PR reverts the behavior to just |
@dunglas guess so, it's considerable though. There will be situations you want to ideally autowire 9/10 methods and face a all or nothing choice: go with autowiring or configure 9 services. We fully lose the autowiring benefit then.. which can be a, pragmatic, pain :-) |
It's still out of the 80/20 use-case, from the looks of it. |
Agree :) edit: consider calls:
- [setBar, [@arg]]
- setFoo # autowired |
Yeah, i think i like autowire: true
[autowire_]calls:
- setBar
- setFoo better than setters_autowired: true Or |
👍 |
1 similar comment
👍 |
Should we consider this for inheritance? At least make children inherit the flag and maybe allow them to change it? Actually doing it afterwards for the 👍 |
I'm worried about usability. If you want full autowiring, it'll now be: autowire: true
autowire_setters: true That's a lot of work for something that's purely for rapid development. But, we need to keep BC also. What about this suggestion, which includes part of @ro0NL's idea: # A) deprecated, but would only autowire the constructor, allows BC
autowire: true
# B) would autowire everything, including setters
autowiring: true
# C) or, use an array to opt into only certain methods
autowiring: [__construct] This is also - accidentally - more flexible. |
Even more flexible, IMHO having to list the methods to autowire would indeed reduce the benefit of using this feature rather than fully fill The proposed flag sounds nice, keeping autowire managing only constructors per default which stay logic to me. Otherwise, what about a choice? autowiring: constructor
autowiring: setters
autowiring: [constructor, setters] (Depreciating the current flag) |
@weaverryan regarding usability, this is a reminder that setter injection is generally harmful, and while it's a nice-to-have, it's usually to be discouraged. |
This, imo, explains why it should be verbose. edit: i rather be explicit in what methods are called (autowired or not). The shortcut ( |
I also agree with @Ocramius, we should discourage setter injection (it should be opt-in). I added initial support for setter autowiring (#17608) to ease the creation of Symfony controllers that don't depend of the container (#18193). Such classes will basically be automatically registered (e.g.: DunglasActionBundle), the user will not have to care about enabling setters autowiring by itself. Developpers wanting to always enable setters autowiring in other contexts can create a userland a compiler pass to do it. |
Is it harmful? The user is already opting into magic. I'm not saying this is wrong, but I want to challenge the statement. Is there some evidence we can look at to support / contest this?
I'm interested in setter autowiring because of the potential to have traits with setters and helper methods - e.g. My proposal (#19631 (comment)) offers BC, ease-of-use and control... but assumes the best default mode for autowiring is constructors and setters. |
Yes, due to basic design of service dependency immutability.
The comment about setter injection can be taken completely out of the scope of this PR or of the dependency injection component. |
Yes, it's basically the same use case. But for such cases, a compiler pass can automatically enable setters autowiring (for instance if the service implements a given interface). |
Ok, I'm getting out-voted, and the arguments are sound. 👍 for the current approach |
Just to clarify; this will call any defined IMO, it's the same trickiness what we currently try to avoid. |
I see. How does this solve optional dependencies? I don't want Point is: you need to know at all time, whenever you define a setter, on whatever level in the class-chain, it's gonna be called (if enabled). It's tricky, period. :) edit: i understand it's somewhat the current behavior, hence i can live with this approach. Just saying ;-) |
What do you think about the following config, it should satisfy everybody's needs and is backward compatible: autowire: true # constructor autowiring
autowire: [__construct, setFoo, setBar] # autowire whitelisted methods only
autowire: '*' # autowire constructor + every setters (following existing rules for setters autowiring) |
What about autowire: true
# enables constructor autowiring by default, if disabled no autowiring whatsoever happens
autowire_calls: [autowireThis, andThat]
# additional method calls to be autowired
autowire_setters: true
# adds all setters to autowire_calls This looks intuitive.. but is limiting in a way, you can only autowire method calls when the constructor is autowired. |
I prefer @dunglas proposal because the various configurations are mutually exclusive, but having several keys allows setting several of them at once, and from a DX perspective requires remembering 3 keys. |
Okay then.. let's do it :) Thanks for letting me clarify my point. |
@dunglas just thought of using bitwise flags, on php level it's pretty verbose; $definition->setAutowired(Definition::CONSTRUCTOR|Definition::SETTERS);
// true = Definition::CONSTRUCTOR
// array = whitelist
if ($definition->getAutowired() & Definition::SETTERS) { } For yaml autowire: !php/const:Definition::FULL or allow the |
|
Understand, hence i proposed allowing the |
My proposal is more flexible and easier to learn (though) |
Easier to learn, i guess so ;-) my proposal allows for adding Anyway, lets move on. |
@ro0NL your proposal shows the wrong Yaml snippet. It would be like this: autowire: !php/const:Symfony\Component\DependencyInjection\Definition::FULL |
Left that out intentionally 👼 i know it's a no-go in some ways (too verbose i guess), but im looking at this from the php side first. |
Status: needs work |
As said, it would be a BC break to don't get this merged for 3.2. |
I'll do it for the 3.2 release. If it's not ready, we'll revert the introduction of setter autowiring (and reintroduce it in 3.3) but I'll find the time to finish this PR. |
I opened a new PR implementing #19631 (comment): #20167 |
…configurable (dunglas) This PR was merged into the 3.3-dev branch. Discussion ---------- [DependencyInjection] Make method (setter) autowiring configurable | Q | A | | --- | --- | | Branch? | master | | Bug fix? | no | | New feature? | yes | | BC breaks? | no | | Deprecations? | maybe? | | Tests pass? | yes | | Fixed tickets | #19631 | | License | MIT | | Doc PR | symfony/symfony-docs#7041 | Follow up of #19631. Implements #19631 (comment): Edit: the last supported format: ``` yaml services: foo: class: Foo autowire: ['__construct', 'set*'] # Autowire constructor and all setters autowire: true # Converted by loaders in `autowire: ['__construct']` for BC autowire: ['foo', 'bar'] # Autowire only `foo` and `bar` methods ``` Outdated: ``` yaml autowire: true # constructor autowiring autowire: [__construct, setFoo, setBar] # autowire whitelisted methods only autowire: '*' # autowire constructor + every setters (following existing rules for setters autowiring) ``` - [x] Allow to specify the list of methods in the XML loader - [x] Add tests for the YAML loader Commits ------- 6dd53c7 [DependencyInjection] Introduce method injection for autowiring
…configurable (dunglas) This PR was merged into the 3.3-dev branch. Discussion ---------- [DependencyInjection] Make method (setter) autowiring configurable | Q | A | | --- | --- | | Branch? | master | | Bug fix? | no | | New feature? | yes | | BC breaks? | no | | Deprecations? | maybe? | | Tests pass? | yes | | Fixed tickets | #19631 | | License | MIT | | Doc PR | symfony/symfony-docs#7041 | Follow up of #19631. Implements symfony/symfony#19631 (comment): Edit: the last supported format: ``` yaml services: foo: class: Foo autowire: ['__construct', 'set*'] # Autowire constructor and all setters autowire: true # Converted by loaders in `autowire: ['__construct']` for BC autowire: ['foo', 'bar'] # Autowire only `foo` and `bar` methods ``` Outdated: ``` yaml autowire: true # constructor autowiring autowire: [__construct, setFoo, setBar] # autowire whitelisted methods only autowire: '*' # autowire constructor + every setters (following existing rules for setters autowiring) ``` - [x] Allow to specify the list of methods in the XML loader - [x] Add tests for the YAML loader Commits ------- 6dd53c7 [DependencyInjection] Introduce method injection for autowiring
This PR follows a discussion with @Ocramius: https://twitter.com/Ocramius/status/754630452045029376
It makes setter injection opt-in.
ping @weaverryan