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

[WIP] Annotation support for Services#21376

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

Closed
weaverryan wants to merge6 commits intosymfony:masterfromweaverryan:di-annotations

Conversation

@weaverryan
Copy link
Member

@weaverryanweaverryan commentedJan 23, 2017
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed tickets#21103
LicenseMIT
Doc PRTODO

Hi guys!

This is a WIP solution for adding annotations to services, and is similar to some proposals in#21103. Notice, this does NOT include a "discovery" mechanism for services: you must still register your service like normal (e.g. in YML or XML file), but then the annotations in the service are used to augment the service Definition. But, if#21289 is merged, then that'll cover automatic "discovery" for these services :).

For me, the BIG motivation is to augment autowiring, when you have just one argument that can't be autowired. Annotations read much better than, for example, setting the index "2" argument in YAML.

_defaults:autowire:truetags:# activates the feature        -{ name: annotated }parameters:mandrill_api_key:XXXXXXXXXservices:App\NewsletterManager:~# you could add tags, arguments or anything else here# these would override the annotations, if they overlap
useDoctrine\ORM\EntityManager;usePsr\Log\LoggerInterface;/** * You could optionally configure any service definition options here: * @DI\Definition(public=false) */class NewsletterManager{private$logger;private$em;private$mandrillApiKey;private$serializer;/**     * @DI\Argument(name="mandrillApiKey", value="%mandrill_api_key%")     */publicfunction__construct(LoggerInterface$logger,EntityManager$em,$mandrillApiKey)    {$this->logger =$logger;$this->em =$em;$this->mandrillApiKey =$mandrillApiKey;    }}

Pretty much anything that you can set in YAML would be supported (most is still a TODO). The feature is currently activated by a tag... which is really ugly (though_defaults at least helps this). We could add this to theDefinition object (like we did withautowire) if we want.

In#22103, there was some talk of following a Spring standard. While I think it IS important to look and learn from them, this keeps very close toour existing standard: effectively re-using all our existing terminology, but in YAML. The@Argument annotation re-uses all the options from the<argument> tag in XML service config.

Cheers!

TODOS

  • Add support for calls
  • Add support for factory, deprecated, configurator, decoratedService, autowiringTypes and autowiring/autowiringMethods
  • Add support for injection on properties
  • Add an@Tag annotation
  • Finish collection and iterator argument types
  • Handle parent class / interface annotations
  • Do not throw an exception when you inherit an annotation from a base class, but that arg doesn't exist on the overridden constructor (would currently throw an error)
  • Add a new "Resource" class so that the container cache is rebuilt (and rebuilt intelligently)
  • Add a lot more tests for many edge cases

enleur, DavidBadura, and LPodolski reacted with thumbs up emojiogizanagi, linaori, jvasseur, mvrhov, iGusev, tadcka, sstok, and mnapoli reacted with confused emoji
*/
public function process(ContainerBuilder $container)
{
if (class_exists(AnnotationReader::class)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if (!class_exists(AnnotationReader::class)) ?

NavyCoat reacted with thumbs up emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

thanks :)

*/
class Service
{
private $shared;
Copy link
Member

Choose a reason for hiding this comment

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

Should we addautowired too?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Definitely - it's one of my todos :)

}
}

private function augmentServiceDefinition(Definition $definition)
Copy link
Member

Choose a reason for hiding this comment

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

It should read recursively annotations from parent classes and implemented interfaces.


$onInvalid = $arg->getOnInvalid();
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
if ('ignore' == $onInvalid) {
Copy link
Member

Choose a reason for hiding this comment

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

===

$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
if ('ignore' == $onInvalid) {
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
} elseif ('null' == $onInvalid) {
Copy link
Member

Choose a reason for hiding this comment

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

===

{
$arguments = array();
$i = 0;
foreach ($method->getParameters() as $parameter) {
Copy link
Member

Choose a reason for hiding this comment

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

Why not using the key instead of the$i variable?

foreach($method->getParameters()as$i =>$parameter) {$arguments[$parameter->getName()] =$i;}

It should do the same thing:https://3v4l.org/Zpq4W

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Updated!

@dunglas
Copy link
Member

I'm still not a fond of having (non-standard) annotations for DI. I've explained my reasons in depth here:#21103 (comment)

However I recognize that it is practical for parameters injection. I've proposed a less intrusive alternative in#20738, but I'm not very convinced by it either.

Maybe should we keep with the current solution for now:

_defaults:autowire:trueparameters:mandrill_api_key:XXXXXXXXXservices:App\NewsletterManager:['', '', '%mandrill_api_key%']# This is already working
sstok and mnapoli reacted with thumbs up emoji

@ogizanagi
Copy link
Contributor

@dunglas :

services:App\NewsletterManager:['', '', '%mandrill_api_key%']# This is already working

This is not already working, until#21313 is merged 😄

dunglas and theofidry reacted with laugh emoji

@weaverryan
Copy link
MemberAuthor

@dunglas I agree with much of what you are saying :), and I see that we're both after trying to solve the "parameter" problem in some decent way. I'm also not convinced by#20738, so we could either stay with the current YAML solution or use annotations (or maybe there's a 3rd option). About your comments (#21103 (comment)), I don't see these as a significant problem. Overall, the fact that this feature would be meant for end-users - and not library authors - puts most of those concerns to rest. And in app code, coupling your code to your framework isn't a problem (actually, I see it as an advantage for speed and clarity). Also, it mayhelp the container to be seen as simply an "internal detail" to end-users. If auto-wiring were perfect, then life would be good. But since there will always be edge-cases (parameters being the big one), with annotations, the user could avoid needing to suddenly (and strangely) go intoservices.yml and add a service key + arguments key (e.g. where the first two args are empty strings). Instead, they would just annotate one arg in their class :).

But, we'll see what others think - I wanted to get some real code to help with this exact discussion!

Cheers!

@GuilhemN
Copy link
Contributor

GuilhemN commentedJan 23, 2017
edited
Loading

For me, the BIG motivation is to augment autowiring, when you have just one argument that can't be autowired. Annotations read much better than, for example, setting the index "2" argument in YAML.

This made me think, can't we have named arguments in yaml? (e.g.

services:Foo:arguments:mandrillApiKey:%mandrill_api_key%

).

Having annotations look huge to me if this is the only motivation, don't you think ?

sstok reacted with thumbs up emoji

@dunglas
Copy link
Member

dunglas commentedJan 23, 2017
edited
Loading

@GuilhemN I've proposed this exact solution to@nicolas-grekas today and I'm working on a PR to implement it 🙌

GuilhemN and sstok reacted with laugh emoji

@weaverryan
Copy link
MemberAuthor

@dunglas@GuilhemN I don't think I agree with that approach, as it's unexpected that changing argument names would have a side effect (this is true with annotations too, but at least the annotation & arg name are together). But, let's open that PR and decide the best solution!

@dunglas
Copy link
Member

Alternative proposal:#21383

@nicolas-grekas
Copy link
Member

Note that to me, this PR and#21383 do not compete. We could very well have both onboard.

return;
}

$this->reader = new AnnotationReader();

Choose a reason for hiding this comment

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

what about using$container->get('annotation_reader') instead? this service is already used at compile time by some other extensions.

Copy link
Member

Choose a reason for hiding this comment

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

and it already causes issues regarding incomplete service configuration (remember the burden when switching the cache configuration in 3.2 ?). We always documented that getting service instances during a compiler pass is an unsupported usage of the container (and so you are on your own and it may break in weird ways), so we should not do it in Symfony itself (especially when it can still break because of a change done by another bundle)

Copy link
Member

@nicolas-grekasnicolas-grekasJan 24, 2017
edited
Loading

Choose a reason for hiding this comment

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

I more than remember since I worked on that again in#21381.
Yet it allowed to fix a core issue, which is that there should be no cache enabled at build time.
Thus, usingannotation_reader could be also seen as a way for us to support using it at compile time a first class citizen - which we have to anyway, as history proved us.


private $lazy;

public function __construct(array $data)
Copy link
Member

Choose a reason for hiding this comment

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

why not removing the constructor and using public properties, letting the annotation parser validate properties itself ? This is the recommended usage of doctrine/annotations when annnotation classes don't need to be reused for other purposes (which is why we don't do in the validator component: constraints themselves are used as annotations, but they are not only annotations)

chalasr reacted with thumbs up emoji
@nicolas-grekasnicolas-grekas added this to the3.3 milestoneJan 24, 2017
fabpot added a commit that referenced this pull requestFeb 13, 2017
…(dunglas, nicolas-grekas)This PR was merged into the 3.3-dev branch.Discussion----------[DependencyInjection] Add support for named arguments| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | yes| Tests pass?   | yes| Fixed tickets | -| License       | MIT| Doc PR        | todoThis PR introduces named arguments for services definitions. It's especially useful to inject parameters in an autowired service. It is (at least partially) an alternative to#21376 and#20738.Usage:```ymlservices:    _defaults: { autowire: true }    Acme\NewsletterManager: { $apiKey: "%mandrill_api_key%" }# Alternative (traditional) syntaxservices:    newsletter_manager:        class: Acme\NewsletterManager        arguments:            $apiKey: "%mandrill_api_key%"        autowire: true``````phpuse Doctrine\ORM\EntityManager;use Psr\Log\LoggerInterface;namespace Acme;class NewsletterManager{    private $logger;    private $em;    private $apiKey;    public function __construct(LoggerInterface $logger, EntityManager $em, $apiKey)    {        $this->logger = $logger;        $this->em = $em;        $this->apiKey = $apiKey;    }}```Commits-------8a126c8 [DI] Deprecate string keys in arguments2ce36a6 [DependencyInjection] Add a new pass to check arguments validity6e50129 [DependencyInjection] Add support for named arguments
@weaverryan
Copy link
MemberAuthor

I'll close this for now (thanks to#21383). We may still want this in the future, to avoid needing to touch a YAML file once you need to map a custom, non-autowired arg... but let's see how this all sorts out.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@nicolas-grekasnicolas-grekasnicolas-grekas left review comments

@stofstofstof left review comments

@dunglasdunglasdunglas requested changes

+1 more reviewer

@dostendostendosten left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

3.3

Development

Successfully merging this pull request may close these issues.

8 participants

@weaverryan@dunglas@ogizanagi@GuilhemN@nicolas-grekas@stof@dosten@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp