Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[DI] Replace wildcard-based methods autowiring by@required annotation#21763

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fabpot merged 2 commits intosymfony:masterfromnicolas-grekas:di-required
Mar 1, 2017

Conversation

@nicolas-grekas
Copy link
Member

@nicolas-grekasnicolas-grekas commentedFeb 25, 2017
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no (affects things that are only on master)
Deprecations?no
Tests pass?yes
Fixed tickets-
LicenseMIT
Doc PR-

While playing a bit with new features in master around DI configuration, several people around me got bitten by wildcard-based autowiring. The typical example is addingautowire: [set*] in_defaults: use that onresource: ../src/Command/ PSR4-based loading and boom,setApplication andsetHelperSet will now be wrongly called. You could tell me "of course, don't to that" - but being bitten so early on a master-only feature makes me really unconfident that this will be easy enough for people after the release.

If wildcard-based autowiring is removed, then I don't see anymore the need for allowing arrays as inautowire: [setFoo,getBar]. Moreover, this array syntax has a core DX issue: it's a dead end as far as the learning curve is concerned. You learn it, then when becoming a more advanced dev, someone teaches you that you'd better use another syntax: explicit wiring.

And in fact, we don't need it at all, because something else already exists: just declare a method call, but don't define its arguments. Ifautowire: true is set, then the AutowiringPass already fills in the holes. There is only one tweak required to make this work: don't autowire optional arguments for method calls - or that'd be a BC break. To my PoV that's even better: this makes autowiring fit a "do the minimum to make it work" strategy. A really good one to me.

But there is still an issue: wildcard-based autowiring fits a need. Namely, it allows one to define a convention (eg.'set*'), and have all such methods that follow the convention be autowired. To me, this looks like doing it reverse (the DI config should adapt to the code, not reverse). So, to fill this need, let the declaration be in the source: just use an annotation!

This PR adds support for the@required annotation, borrowed from the Spring framework:
https://www.tutorialspoint.com/spring/spring_required_annotation.htm

Using the annotation is totally optional of course. If you do,and if autowiring is on, then it'll be autowired. If you don't, nothing changes: do manual wiring.

Even when not using autowiring, the annotation is still a nice hint for the consumer of your classes: it tells the reader that this method needs to be called for correct instantiation - thus lowering one drawback of setter injection (discoverability).

The implementation of the annotation parsing is done using a few regexp (no dep on any complex parser) - and works with inheritance, by leveraging the@inheritdoc tag (the default behavior being tonot inherit anything from parent methods).

All in all, looking at the diff stats, it makes everything simpler. Good sign, isn't it?

theofidry, ro0NL, and apfelbox reacted with thumbs up emojisstok reacted with heart emoji
@weaverryan
Copy link
Member

I like it! It removes 2 things I don't like:

  • I never liked theautowire key with actual args (autowire: [setFoo,getBar]). It moves more config into YAML, with limited benefit.

  • Since theset* thing has proven to be a "gotcha", we should definitely remove it: we're trying to walk a balance of added convenience, but no significant "wtf" increases!

There is only one tweak required to make this work: don't autowire optional arguments for method calls - or that'd be a BC break. To my PoV that's even better: this makes autowiring fit a "do the minimum to make it work" strategy. A really good one to me.

Yes! I agree - autowiring should do as little as possible (based on the code you've written).

And explaining autowiring should now be pretty simple!

By addingautowire: true, Symfony will read the type-hints on your__construct()
method and automatically try to inject services that match those type-hints. You can
also tell Symfony to automatically call (and autowire) other methods (e.g.setLogger())
by adding an@required annotation above the method. This also works for
getter-injected methods.

nicolas-grekas and dunglas reacted with thumbs up emoji

@mvrhov
Copy link

I like the autowire feature, but I don't like this proposal. Our company has no annotations policy, so if I understand correctly this means no autowire for us.

@ro0NL
Copy link
Contributor

ro0NL commentedFeb 26, 2017
edited
Loading

just declare a method call, but don't define its arguments

Some of my first thoughts :)#19631 (comment) i think it's a good convention 👍

don't autowire optional arguments for method calls

With named arguments we could define optional ones, override required ones, and let autowiring fill in the remaining required ones.. right?

And shouldnt this be allowed by the annotation-form as well? I.e.@MethodCall({'$arg': '@ref'}) (i think i'd prefer@MethodCall over@Required).

@ro0NL
Copy link
Contributor

I see@required is a specific built-in feature, where i was thinking of annotation support like we do for routes/validation (ie.@DI\MethodCall, etc.), my bad. Im not even sure we should go that way..

@nicolas-grekas
Copy link
MemberAuthor

nicolas-grekas commentedFeb 26, 2017
edited
Loading

so if I understand correctly this means no autowire for us.

@mvrhov I think you did not understand correctly: if you don't want to use the annotation, that won't prevent you from using autowiring. First, constructor autowiring doesn't require it. Then, for setters and getters, that will just make you declare which of them to autowire in the DI config, instead of in the code. It has to be somewhere. The annotation allows code authors to help autowiring users so that they can save these DI config lines. Only that.

With named arguments we could define optional ones, override required ones, and let autowiring fill in the remaining required ones.. right?

@ro0NL Right!

And shouldnt this be allowed by the annotation-form as well? I.e.@MethodCall({'$arg': '@ref'}) (i think i'd prefer@MethodCall over@Required)

Yes, this shouldn't be allowed, because this is moving the DI configuration right into the code - which isnot what this PR does - and it a totally different topic.

I see@required is a specific built-in feature, where i was thinking of annotation support like we do for routes/validation (ie.@DI\MethodCall, etc.), my bad. Im not even sure we should go that way..

Same here, we shouldn't go that way to me, too much added complexity for no benefit at all.

@linaori
Copy link
Contributor

linaori commentedFeb 26, 2017
edited
Loading

I will see a lot of people remove@required from the docblock because:

  • It's not something used in docblocks
  • It's not in the expected annotation format used by Doctrine Annotations

@fabpot
Copy link
Member

First of all, I'm really happy that we will remove theget* andset* support in the autowire configuration. I did test them and it just does not work well in real-life projects.

That being said, having "something" to autowire dependencies in a lazy-way would be a great feature for Symfony 3.3. And we are almost there. I've migrated several projects and implemented all the new features of the container, and I have to say that this is really a joy to work with (I actually encourage anyone to try it on their projects and give feedback).

The proposed@required annotation fits a hole that would finish our container enhancement journey quite nicely. It's a very elegant solution for what we are trying to do.

If we look at the semantic of the proposed@required annotation closely, I think it's very different from the annotations we manage inFrameworkExtraBundle. It seems to me that it is more similar to the@deprecated or@final annotations.

Those annotations gather generic and purely descriptive semantic about some methods/classes, they have nothing specific about Symfony and can/could be used by any PHP code. Note that we use the semantic they convey in the Debug component, in DebugClassLoader, to give a better DX, so we have a precedent here.

I think all of this applies also to the@required annotation: it can benefit to any PHP code, and allows to declare which parts of a class are required for full instantiation, apart from the constructor. While the generalization of this annotation is not the target, it sounded like an important criteria to understand its usefulness to me. This is to be opposed to Doctrine annotations, which convey domain specific semantics, and thus wouldn't be suited here.

The fact that this annotation is not yet standardized shouldn't prevent us from adopting it (we did so for@final already).

This is also very different from previous proposals that tried to move configuration into annotations. Using the@required annotation when present is purely optional and is only triggered when you enable the autowiring.

@fabpot
Copy link
Member

👍

@fabpot
Copy link
Member

Thank you@nicolas-grekas.

nicolas-grekas reacted with hooray emoji

@fabpotfabpot merged commitf286fcc intosymfony:masterMar 1, 2017
fabpot added a commit that referenced this pull requestMar 1, 2017
…quired` annotation (nicolas-grekas)This PR was squashed before being merged into the 3.3-dev branch (closes#21763).Discussion----------[DI] Replace wildcard-based methods autowiring by `@required` annotation| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no (affects things that are only on master)| Deprecations? | no| Tests pass?   | yes| Fixed tickets | -| License       | MIT| Doc PR        | -While playing a bit with new features in master around DI configuration, several people around me got bitten by wildcard-based autowiring. The typical example is adding `autowire: [set*]` in `_defaults`: use that on `resource: ../src/Command/` PSR4-based loading and boom, `setApplication` and `setHelperSet` will now be wrongly called. You could tell me "of course, don't to that" - but being bitten so early on a master-only feature makes me really unconfident that this will be easy enough for people after the release.If wildcard-based autowiring is removed, then I don't see anymore the need for allowing arrays as in `autowire: [setFoo,getBar]`. Moreover, this array syntax has a core DX issue: it's a dead end as far as the learning curve is concerned. You learn it, then when becoming a more advanced dev, someone teaches you that you'd better use another syntax: explicit wiring.And in fact, we don't need it at all, because something else already exists: just declare a method call, but don't define its arguments. If `autowire: true` is set, then the AutowiringPass already fills in the holes. There is only one tweak required to make this work: don't autowire optional arguments for method calls - or that'd be a BC break. To my PoV that's even better: this makes autowiring fit a "do the minimum to make it work" strategy. A really good one to me.But there is still an issue: wildcard-based autowiring fits a need. Namely, it allows one to define a convention (eg. `'set*'`), and have all such methods that follow the convention be autowired. To me, this looks like doing it reverse (the DI config should adapt to the code, not reverse). So, to fill this need, let the declaration be in the source: just use an annotation!This PR adds support for the `@required` annotation, borrowed from the Spring framework:https://www.tutorialspoint.com/spring/spring_required_annotation.htmUsing the annotation is totally optional of course. If you do, *and if autowiring is on*, then it'll be autowired. If you don't, nothing changes: do manual wiring.Even when not using autowiring, the annotation is still a nice hint for the consumer of your classes: it tells the reader that this method needs to be called for correct instantiation - thus lowering one drawback of setter injection (discoverability).The implementation of the annotation parsing is done using a few regexp (no dep on any complex parser) - and works with inheritance, by leveraging the `@inheritdoc` tag (the default behavior being to *not* inherit anything from parent methods).All in all, looking at the diff stats, it makes everything simpler. Good sign, isn't it?Commits-------f286fcc [DI] Replace wildcard-based methods autowiring by `@required` annotation9081699 Revert "minor#21315 [DI][FrameworkBundle] Show autowired methods in descriptors (ogizanagi)"
@nicolas-grekasnicolas-grekas deleted the di-required branchMarch 1, 2017 20:50
@dunglas
Copy link
Member

Too late but 👍 too, definitely better thanset*.

@fabpot
Copy link
Member

Playing with this now, there is one catch, it does not play well with Doctrine Annotations, which wants to parse everything.

So, we need to add:

\Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredName('required');

@fabpot
Copy link
Member

// should be called
}

/** @inheritdoc*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It lacks a test for a child method with no dock block and a parent method marked with@required.

@kbond
Copy link
Member

Does this mean setter autowiring is no longer possible withContainerAwareTrait?

@nicolas-grekas
Copy link
MemberAuthor

of course not, nothing changed on that side :)

@nicolas-grekas
Copy link
MemberAuthor

oh sorry, too fast! maybe add the annotation to the trait for such cases? would you like to submit a PR?

@kbond
Copy link
Member

kbond commentedMar 23, 2017
edited
Loading

@nicolas-grekas
Copy link
MemberAuthor

Sure, then it's a yes, but I don't see this as a limitation. Use instanceof conditionals instead

@kbond
Copy link
Member

I got it working with_instanceof but I had to have my service implementContainerAwareInterface.

@nicolas-grekas
Copy link
MemberAuthor

Next step could be implementing ServiceSubscriberInterface :) see#21708

@kbond
Copy link
Member

Haha neat, thanks! This autowiring stuff is moving fast! :)

@fabpotfabpot mentioned this pull requestMay 1, 2017
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@dunglasdunglasdunglas left review comments

Assignees

No one assigned

Projects

None yet

Milestone

3.3

Development

Successfully merging this pull request may close these issues.

9 participants

@nicolas-grekas@weaverryan@mvrhov@ro0NL@linaori@fabpot@dunglas@kbond@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp