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
Replacing object definitions: inconsistent behavior of DI\object() #294
Comments
Okay, maybe I'm just asking for The inconsistency is still an issue though. |
I completely understand. To be honest the decision on this one was a tough one, I went with the current behavior (always extend) because of:
I have played with a lot of different syntaxes when working on v5 (e.g. The solution I considered but didn't implement yet (feel free to suggest another) is to add a new Anyway, I understand your trouble. I want to find a solution to this too. Not extending by default was creating a terrible developer experience from what I've tested. Especially since most users don't picture autowiring as a definition you can extend ( So feel free to suggest. |
Okay, so here's what I would suggest:
Both Deprecating Does it make sense to Having ConnectionConfig::class => create(), And another file might read: ConnectionConfig::class => extend(function (ConnectionConfig $config) {
$config->username = 'root';
$config->password = '12345678';
}), Although I'm not a big fan of duplicating the type-hint here, it's required for IDE support. We also added a configure method to $container->configure(function (ConnectionConfig $config) {
$config->username = 'root';
$config->password = '12345678';
}); The Not sure if I'm getting sidetracked here, sorry. I've been coding for 20 hours in two days :-) |
I think the ConnectionConfig::class => decorate(function (ConnectionConfig $config) {
$config->username = 'root';
$config->password = '12345678';
// Don't forget to return the new or updated object!
return $config;
}), But you could also even do it with // config-base.php
ConnectionConfig::class => object(), // config-local.php
ConnectionConfig::class => object()
->property('username', 'root')
->property('password', '12345678'), What I have in mind for Again for this the main use case is for autowiring: you want to be able to define what to inject in a constructor parameter that isn't type-hinted. The second use case is to be able to override a parameter/property injection in another config file (e.g. the example above). The third, minor, use case is to extend a parent config, here is how Symfony does it: http://symfony.com/doc/current/components/dependency_injection/parentservices.html So to sum up:
So |
I can, but it's intended to replace values rather than updating existing values - if you forget to return the same value at the end, you accidentally remove the value. This is great for e.g. scalar values, but for objects, I'd prefer to have a function that just performs further initialization on an existing object without replacing anything, e.g. without the required
I could, but I try to avoid the DSL and strongly favor using closures, because using the DSL breaks automated refactoring and IDE inspections, due to the fact there is no static coupling. I strongly prefer using PHP code when possible, rather than making weak references to parameter names, method-names, etc. - if I can't trust automated refactoring, I feel I'm doing it wrong; I want all my code to pass inspections and survive simple operations like renaming a property, method or parameter, changing parameter order, etc.
Well, decorate only decorates if you choose to decorate - the name is a bit misleading in that sense, as what you're actually doing, is replacing the value. Whether you replace it with a decorator or something different is really up to you. |
OK I see. However I'm not 100% convinced (personally) that's such a big issue, because in the end it's just a matter of remembering to return the value.
Fair enough, totally valid reasons.
Yep, the goal was to provide a I wonder if adding Maybe the documentation can be improved to show 2 examples for |
Yeah, I don't personally want to write integration tests for every container configuration ;-)
Okay, makes sense. So, what do you think about just changing the current behavior, so that, if the function doesn't return anything, it keeps the current value? Is there a real use-case where you would want to deliberately replace a current value with null? If not, we can simply change it so that returning a value means replace it, and not returning a value means don't - seems pretty intuitive? |
it does sound good but…
unfortunately yes :( It's a use case I've seen in Piwik for example, but can't remember exactly what it was. Anyway that's definitely valid (configuring e.g. optional dependencies). I also thought of that solution but the goal was to throw an exception if decorate() doesn't return anything. Your suggestion is maybe better. But in any case, it's not really doable I think. |
This long standing issue has finally been addressed and will be fixed in PHP-DI 6 (currently the
For more information, see the pull request: #449 |
Per the manual:
So then, what do you do if you need to replace a previous object definition?
This may be the most common use-case, but it's not the only use-case.
Even had that in bold, because this behavior is inconsistent with the rest of the API.
I would strongly prefer a consistent API to lessen the learning curve (we're a big team) and a dedicated additional function for extending a previous definition, e.g.
DI\extend()
or maybeDI\override()
to make this an explicit operation. (Implicit behavior is risky. Behavior that varies based on previous method calls is confusing.)This would also eliminate the case where you try to "extend" something that hasn't been defined at all, and potentially end up with something that partially works, but probably not as intended.
(PS: I've opened a few issues by now - feel free to ask me to PR on any of those)
The text was updated successfully, but these errors were encountered: