-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DependencyInjection] Add #[Target]
to tell how a dependency is used and hint named autowiring aliases
#40800
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
44a833d
to
7d99272
Compare
Hey! I think @fancyweb has recently worked with this code. Maybe they can help review this? Cheers! Carsonbot |
7d99272
to
3688d4e
Compare
Hi! Thanks for always thinking of new ways to frame & solve these problems :). function __construct(#[Purpose('review.state_machine')] WorkflowInterface $workflow) It looks like function __construct(#[Purpose('review.state_machine')] $workflow) I... think I "get" what the idea is... but it doesn't feel happy to me yet. I would still prefer to configure this in my service config... especially if that config is written in PHP :) |
Except that the corresponding service identifier is not necessarily
Right now, this is not something that is configured in any of these files: bundles declare named autowiring aliases with :) |
I love the idea. TBH, I never use argument name autowiring because I want my constructor argument to be However, I have a concern about the attribute name too. Purpose seems vague. Could it be something more explicit? |
I like this proposal! Thank you! The name function __construct(#[Purpose('review.state_machine')] WorkflowInterface $workflow) {}
function __construct(#[Inject('review.state_machine')] WorkflowInterface $workflow) {} |
The |
Naming things... :)
Purpose is the most accurate to me, but the 2 others found fit also. |
Why? We could use the same attribute for another feature? |
Because that's what "description" means... :) |
Could we correlate the attribute name with how we use it, for example |
I think that's too technical yes (and not accurate enough also: this is not a named autowiring alias). When you decide to name an argument I think that this attribute should keep that conceptual decoupling. Ie we should provide a way for class authors to make their classes more descriptive. Then, later on, if this added description is useful to autowiring, that's a match and a win. If we don't do this, we miss all the conceptual benefits of descriptive programming IMHO. |
Thank you for the explanation. I understand it better now but there is still a difference for me. Originally, there is a decoupling because having an argument, whatever its name, is something required. However, using the attribute is optional. As a Symfony user, I'm not going to use the attribute to describe my argument, I have PHP comments for that. If I use the attribute, it's because I know something precise will happen. I like the idea of the conceptual decoupling though because we could maybe reuse it for another future feature and then describing one time the arg for many benefits would be awesome. But atm, IMHO, it will be easier for a random Symfony user to understand what the attribute is doing if its name is less generic. |
@fancyweb PHP comments are only about describing it for humans (as they are unstructured). PHP 8 attributes are precisely about describing the code for machines |
a generic concept cannot have a less generic name :) to me being able to use local naming, eg. we can argue the attr should be moved to the service contract layer eventually to convey decoupled concepts, rather than being Symfony/DI specific still. What i like to propose is to make the list of "purposes" explicit. Eg. |
im also curious if registerAliasForAutowiring should be called registerPurposeForAutowiring with optin to aliasing (thus have both) edit: it's called registerAliasForArgument and $name can be the purpose name, never mind -_- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool. I also like this feature.
From a user's perspective. I don't really understand "Purpose". In my mind it sound more like a "wish" or "intent". Ie, "I like to have a 'review.state_machine', but it does not matter too much what I get".
I like Target
and Inject
.
src/Symfony/Component/DependencyInjection/Attribute/Purpose.php
Outdated
Show resolved
Hide resolved
see also https://english.stackexchange.com/questions/323400/intention-vs-purpose i tend to agree "purpose" sounds more explicit towards the outcome, whereas "intended" would only imply a certain outcome |
Where can a user "look up" the possible values (e.g. A lot of people seem to dislike Also, what error would I get if I passed an incorrect value? And what error would I get if I used this in a totally inappropriate spot, like for an argument that has no type-hint at all... or an argument that does have a type-hint, but where there is only one autowireable "type"?
For "humans", we can just describe the documentation in phpdoc. I understand that this attribute is specifically so that we can describe the purpose of an argument in a machine-readable language. And so, is there any other concrete usage of Cheers! |
purpose: i shall use this workflow for reviewing only personally, as a class author, i'd like to convey the first: "i cannot use this workflow for anything but reviewing" regardless of how i name my arguments ($flow, $workfFlow, $service, $totallyUnrelated). if the system injects a single autowireble type which should not be used for "some purpose", that's a user's config bug IMHO. At this point it couldnt provide anything else, and the user's config may very well be implying a single worflow fits all purposes 🤷 i think purpose + arg without typehint can be a compile error yes 🤔 i've no other use cases in mind yet, but |
I would still use (1) (2) If you run
(3) In the Symfony Docs (https://symfony.com/doc/current/workflow.html#accessing-the-workflow-in-a-class) we say this: use App\Entity\BlogPost;
use Symfony\Component\Workflow\WorkflowInterface;
class MyClass
{
private $blogPublishingWorkflow;
// this injects the blog_publishing workflow configured before
public function __construct(WorkflowInterface $blogPublishingWorkflow)
{
$this->blogPublishingWorkflow = $blogPublishingWorkflow;
}
// ...
} Summary:
function __construct(#[Inject('review.state_machine')] WorkflowInterface $workflow) {} |
#[Purpose]
to tell how a dependency is used and hint named autowiring aliases#[Target]
to tell how a dependency is used and hint named autowiring aliases
3688d4e
to
4b3eb34
Compare
4b3eb34
to
81810cb
Compare
We should still be true to SOLID, and to "Inversion of Control".
This comment would be wrong. This does not inject anything. Symfony injects (IoC rules), and Symfony might read that attribute, but only if instructed to do so. I hope the doc will be careful into explaining things in the correct order, which is: classes declare their dependencies, and the outside world is responsible for providing the appropriate dependencies that fit both the need of the class, and the need of that outside world. A better wording would be "Symfony will inject the blog_publishing workflow configured before". |
I updated the PR to use |
81810cb
to
5f430af
Compare
Im happy with using From my (the users) point of view: Im writing a class, I need some dependencies and I do want my arguments to map/target another service I've written. I can define that an argument will "target" a specific service. I understand that it is wrong, but this is my mindset when I write a service. |
@javiereguiluz |
5f430af
to
0cde234
Compare
The exact same errors that you get when using named autowiring aliases already. That is: if a default autorwiring alias exists you'll get the corresponding service and no error, otherwise you'll get the suggestions computed by AutowiringPass. @lyrixx mentioned that these suggestions could be improved and he's right, but that's unrelated to this PR.
Nothing, as expected, because declarative programming means that.
Not yet, future will tell :) I think |
0cde234
to
e3e8caf
Compare
…d and hint named autowiring aliases
e3e8caf
to
cc76eab
Compare
Thank you |
This PR was submitted for the 5.x branch but it was merged into the 4.4 branch instead. Discussion ---------- [Workflow] Updated wording on comment This comes from a suggestion in symfony/symfony#40800 Commits ------- 6f2c11a [Workflow] Updated wording on comment
Right now, when one wants to target a specific service in a list of candidates, we rely on the name of the argument in addition to the type-hint, eg:
function foo(WorkflowInterface $reviewStateMachine)
The deal is that by giving the argument a name that matches the target use case of the required dependency, we make autowiring more useful.
But sometimes, being able to de-correlate the name of the argument and the purpose is desired.
This PR introduces a new
#[Target]
attribute on PHP8 that allows doing so. The previous example could be written as such thanks to it:function foo(#[Target('review.state_machine')] WorkflowInterface $workflow)
That's all folks :)