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 container injection by explicit service locators#21553

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 1 commit intosymfony:masterfromchalasr:service-locator
Feb 13, 2017

Conversation

@chalasr
Copy link
Member

@chalasrchalasr commentedFeb 7, 2017
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed tickets#20658
LicenseMIT
Doc PRsymfony/symfony-docs#7458

This adds a newServiceLocatorArgument (!service_locator) argument type which takes a list of services, meant to be used as a concrete service locator in order to avoid the remaining needs for injecting the container when it's only a matter of dependency lazy-loading.

Config:

App\FooBar:[!service_locator { key1: '@service1', key2: '@service2' }]
<serviceclass="App\FooBar"public="false">    <argumenttype="service-locator">        <argumenttype="service"key="key1"id="service1"/>        <argumenttype="service"key="key2"id="service2"/>     </argument></service>
newServiceLocatorArgument(array('key1' =>newReference('service1'),'key2' =>newReference('service2'));

Usage:

$locator->has('key1')// true$locator->has('service1')// false, the defined key must be used$locator->get('key1');// service1 instance$locator->get('service1');// exception$locator->has('not-specified')// false$locator->get('not-specified');// exception

We have some concrete use cases in the core where this would be useful (see e.g. SecurityBundle's FirewallMap), same in userland/3rd party code (see related RFC).

ogizanagi, linaori, apfelbox, ro0NL, Koc, enleur, and yceruto reacted with thumbs up emojiogizanagi, robfrawley, and sstok reacted with hooray emoji
@ogizanagi
Copy link
Contributor

ogizanagi commentedFeb 7, 2017
edited
Loading

WDYT about using tags on each service available through a locator instead of listing the services as the locator arguments?

Something like:

<serviceid="MyServiceAvailableThroughALocator"class="..."><!-- ...-->    <tagname="service.locator"locator="locator_name"alias="service_alias_within_the_locator" /></service>

Then any service can declare it is available through a named locator, and can provide an alias used to identify it within the locator itself.

@jvasseur
Copy link
Contributor

Does it allow mapping keys to services like this ?

App\FooBar:arguments:        -=locator:foo:'@service1'bar:'@service2'

I think most of the time it's what is needed since you need to map the service id to some kind of alias used by the component receiving the locator.

ogizanagi and chalasr reacted with thumbs up emoji

@chalasr
Copy link
MemberAuthor

@ogizanagi As discussed, I find more adapted to let the locator consumers defining explicitly which services they need rather than asking each service to define from which service locator(s) they can be accessed. The service being already in the container, to me it's fine to request it, as when using other lazy arguments. I don't see the need for extending the ServiceLocator itself in userland, nor restricting which services should be accessed by a locator elsewhere than the service using the locator itself, even if the idea is interesting.
Discussion stays open if others think it's a good idea.

@jvasseur Right now it doesn't, but it makes sense to me and adding it should not be hard.

@stof
Copy link
Member

stof commentedFeb 7, 2017

@ogizanagi when a service needs a service locator, it has a single service locator, not multiple ones.
Services needing a service locator will often fill it using a tag. But it is better to keep using separate tags for each use case instead of transforming them all into<tag name="service.locator" locator="..." />

@ogizanagi
Copy link
Contributor

ogizanagi commentedFeb 7, 2017
edited
Loading

@stof

when a service needs a service locator, it has a single service locator, not multiple ones.
Services needing a service locator will often fill it using a tag. But it is better to keep using separate tags for each use case instead of transforming them all into

I think you misunderstood me (or am I misunderstanding you?). I updated my comment since, though, in order to show a more complete sample.
I don't want to tag a service needing a locator instance, but instead tag each service being available through a locator (maybe theservice.locator tag name isn't right).
This means being able to extend the list of available services of a well identified locator on userland or from third-party bundles.

@stof
Copy link
Member

stof commentedFeb 7, 2017

@ogizanagi bundles wanting to make the list extendable should use their own tags, allowing to keep things more understandable, and more flexible (the bundle can do extra processing of the tagged services in the compiler pass, or expect additional attributes).
Forcing to handle all collecting in a single compiler pass defined in Symfony itself isless powerful for bundles, and has a worse DX (and it leaks implementation details about the usage of the ServiceLocator feature in the bundle, while the bundle should be free to refactor the way it actually injects the services).

@stof
Copy link
Member

stof commentedFeb 7, 2017

thus, it would also force to add an identifier on locators (to reference them in your proposed tag), which goes against the architecture of arguments

@chalasr
Copy link
MemberAuthor

This means being able to add services to a well identified locator on userland or from third-party bundles.

I'm not sure about the usefulness of defining service locators as services, as use cases are quite specific (most of the times only one service needs a given range to be accessible f.i. CommandBus, FirewallMap).

If one needs two services with the same locator, then the services should be quite related IMO, so it can be solved by defining a parent service defining the locator argument and children would inherit this argument (e.g. Console command needing access to console helpers).
Right now I don't see a concrete need for having well identified locators nor creating custom ServiceLocator implementations in userland.

@ogizanagi
Copy link
Contributor

@stof : Understood. Finally, I agree. Thanks.

@chalasr
Copy link
MemberAuthor

@jvasseur Updated to support mapping keys to services

jvasseur reacted with thumbs up emoji

$getterCode .=' }';

$hasserCode ='function ($id) {'."\n";
$hasserCode .=sprintf(' $references = array(%s);',implode(',',$servicesMap));
Copy link
Contributor

@TaluuTaluuFeb 7, 2017
edited
Loading

Choose a reason for hiding this comment

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

sprintf('$references = %s;', var_export($services, true)); ?

(Sorry, moved my comment here, as it seemed more appropriate)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@Taluu I tried it and in fact we only need a string map here, whilevar_export dumps Reference objects. Also looping over references is actually needed for generating the getter code so I think it can be kept as is, unless another existing helper exists for such

$hasserCode ='function ($id) {'."\n";
$hasserCode .=sprintf(' $references = array(%s);',implode(',',$servicesMap));
$hasserCode .="\n\n".' return isset($references[$id]) || in_array($id, $references, true);';
$hasserCode .="\n }";
Copy link
Contributor

Choose a reason for hiding this comment

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

More a question of tastes and all, but instead of several concatenations, I would have used a nowdoc instead :

<?php// ...$hasserCode = <<<'EOD'$references = ...EOD;

Not excluding a call to sprintf afterwards, of course

*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class LocatorArgumentextends IteratorArgument
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 not extend IteratorArgument. A LocatorArgument isnot an IteratorArgument (something checking for iterator arguments should probablynot be triggered for locator arguments)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

It was a mistake, fixed.

@chalasrchalasrforce-pushed theservice-locator branch 5 times, most recently from5a6a226 tob2b185fCompareFebruary 7, 2017 19:15
@nicolas-grekasnicolas-grekas added this to the3.x milestoneFeb 7, 2017
Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

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

the map must be explicitly keyed

private$values;

/**
* @param Reference[] $values

Choose a reason for hiding this comment

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

doesn't have to be only references - the yaml loader currently supports any kind, which is nice I think - same for the xml loader

Copy link
MemberAuthor

@chalasrchalasrFeb 7, 2017
edited
Loading

Choose a reason for hiding this comment

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

Does that mean$v = $v instanceof Reference ? $v : new Reference($v); should be used, or do you have something else in mind (keep scalars, parameters)?

Choose a reason for hiding this comment

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

Nope, the values can really be anything, references or arrays or scalars

Copy link
MemberAuthor

@chalasrchalasrFeb 7, 2017
edited
Loading

Choose a reason for hiding this comment

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

GivenContainerInterface::get() must return an object, scalars should be returned bygetParameter(), right?

/**
* @author Robin Chalas <robin.chalas@gmail.com>
*/
finalclass ServiceLocator

Choose a reason for hiding this comment

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

should implement ContainerInterface to me

ogizanagi reacted with thumbs up emoji

Choose a reason for hiding this comment

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

Should we mark it as internal also so that we discourage it from being used in type hints?

ogizanagi, jvasseur, and chalasr reacted with thumbs up emoji
Copy link
MemberAuthor

@chalasrchalasrFeb 7, 2017
edited
Loading

Choose a reason for hiding this comment

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

should implement ContainerInterface to me

I hesitated to, sounds good to me. However, since yaml/xml file loaders can return references, should we just ignore their invalid behavior and use the one passed toServiceLocator::get()?
Also, should it make somenoop for e.g.get/setParameter(), or should it truly implement that?

Copy link
Contributor

@jvasseurjvasseurFeb 7, 2017
edited
Loading

Choose a reason for hiding this comment

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

Maybe we could make it implement the ArrayAccess interface. It would allow consumer interfaces to usearray|ArrayAccess as a type hint and use it like an array.

Copy link
Contributor

Choose a reason for hiding this comment

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

@nicolas-grekas : Just to be sure: You're talking about PSR-11'sContainerInterface, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes psr-11 will solve part of the problem (it solves the coupling with the symfony framework).

But let's say we use this feature for some symfony component (Form Types could be an example), if i want to use this component outside the symfony framework I would need to : require an psr-11 container, instantiate it with the services I want and then pass it to the component when I could just use a PHP array.

Copy link
Contributor

Choose a reason for hiding this comment

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

You're just looking for psr-11

PSR-11 is still about DI containers. It semantically couples the code with dependency injection container concept. This is odd: I don't want to know whether it is container or simple array-like map of "command handlers". DI container is just specific type of a map.

chalasr and jvasseur reacted with thumbs up emoji

Choose a reason for hiding this comment

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

@jvasseur@unkind you're missing eg exceptions - the wording about them in PSR-11 is a requirement here also. ArrayAccess is not an interface - it's just an magical incantation for the engine.

to use this component outside the symfony framework I would need to : require an psr-11 container

???: thiswould be an implementation of PSR-11, so you'd need nothing else really.

chalasr reacted with thumbs up emoji
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm talking about using a component outside of a DI container so yes you would need to require one.

Copy link
Contributor

Choose a reason for hiding this comment

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

you're missing eg exceptions - the wording about them in PSR-11 is a requirement here also

What exactly do we miss? You shouldn't access array field without checking whether it exists. So you won't face theNotFoundException (the only checked exception in PSR-11, btw) or your code is just broken.

}elseif ($valueinstanceof LocatorArgument) {
$references =$value->getValues();
$value =newServiceLocator(function ($id)use ($references) {
returnisset($references[$id]) ||in_array($id,array_map(function ($ref) {return (string)$ref; },$references),true);

Choose a reason for hiding this comment

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

the in_array part looks wrong to me - keys must be the ony way to reference values

ogizanagi and jvasseur reacted with thumbs up emoji
return$this->get($references[$id],$references[$id]->getInvalidBehavior());
}

return$this->get($id,$references[array_search($id,$references)]->getInvalidBehavior());

Choose a reason for hiding this comment

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

keys must be the only way to reference values

$getterCode .=$singleGetter;
}
$getterCode .=' }';
$hasserCode ='function ($id) {'."\n";

Choose a reason for hiding this comment

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

given that keys should be the only way to reference values, instead of hasser+getter, I'd suggest generating a singleClosure[], keyed by "public" service name

ogizanagi and chalasr reacted with thumbs up emoji
@nicolas-grekas
Copy link
Member

nicolas-grekas commentedFeb 7, 2017
edited
Loading

This should be marked as experimental also.
And thanks for working on it :)

@chalasr
Copy link
MemberAuthor

Status: needs work

😅

private$values;

/**
* @param array $values An array of mixed entries indexed by identifier
Copy link
Member

Choose a reason for hiding this comment

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

That does not help that much. But now I realize that I've missed something in this PR. This allows to inject both services and parameters, right? But a service locator is really about giving access to services only, not parameters. Any use case for having parameters as well?

Choose a reason for hiding this comment

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

That's PSR-11 - return type of "get" is mixed for this reason - same as eg Pimple ;)
I see no reason to forbid non-objects - that won't provide any technical benefit (even if I won't use that much myself).

ogizanagi reacted with thumbs up emoji
Copy link
MemberAuthor

@chalasrchalasrFeb 13, 2017
edited
Loading

Choose a reason for hiding this comment

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

Please see#21553 (comment). There's no explicit notion of parameters here, only services toget() but services can be any value. I have no strong opinion about, but that's PSR-11 compliant and I don't see any reason to forbid it in fact.

Copy link
Member

Choose a reason for hiding this comment

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

Let me be very clear: I don't care about PSR-11. Let's go back to the basics instead. A service locator must return services. So, let's do that.

And as far as Pimple is concerned, please keep in mind that this is a toy, a joke, not the holy grail of dependency injection, so mentioning it here is pointless :)

HeahDude reacted with heart emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Docblock and tests updated to expect only references.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

It now throws on non-reference values

@chalasrchalasrforce-pushed theservice-locator branch 2 times, most recently frombb455aa to34443bbCompareFebruary 13, 2017 09:47
private$values;

/**
* @param array $values An array of references entries indexed by identifier

Choose a reason for hiding this comment

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

Reference[] + validity check in the constructor? maybe also in loaders if that can provide more useful error messages?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

done

@chalasrchalasrforce-pushed theservice-locator branch 2 times, most recently fromd4db8fc to1c0c22dCompareFebruary 13, 2017 10:02
[SecurityBundle] Avoid container injection in FirewallMap
@fabpot
Copy link
Member

Thank you@chalasr.

@fabpotfabpot merged commite7935c0 intosymfony:masterFeb 13, 2017
fabpot added a commit that referenced this pull requestFeb 13, 2017
…ocators (chalasr)This PR was merged into the 3.3-dev branch.Discussion----------[DI] Replace container injection by explicit service locators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets |#20658| License       | MIT| Doc PR        |symfony/symfony-docs#7458This adds a new `ServiceLocatorArgument` (`!service_locator`) argument type which takes a list of services, meant to be used as a concrete service locator in order to avoid the remaining needs for injecting the container when it's only a matter of dependency lazy-loading.Config:```yamlApp\FooBar: [!service_locator { key1: '@Service1', key2: '@service2' }]``````xml<service public="false">    <argument type="service-locator">        <argument type="service" key="key1"/>        <argument type="service" key="key2"/>     </argument></service>``````phpnew ServiceLocatorArgument(array('key1' => new Reference('service1'), 'key2' => new Reference('service2'));```Usage:```php$locator->has('key1') // true$locator->has('service1') // false, the defined key must be used$locator->get('key1'); // service1 instance$locator->get('service1'); // exception$locator->has('not-specified') // false$locator->get('not-specified'); // exception```We have some concrete use cases in the core where this would be useful (see e.g. SecurityBundle's FirewallMap), same in userland/3rd party code (see related RFC).Commits-------e7935c0 [DI] Replace container injection by explicit service locators
@chalasrchalasr deleted the service-locator branchFebruary 14, 2017 15:43
@lsmith77
Copy link
Contributor

https://github.com/liip/LiipContainerWrapperBundle :)

sstok, hhamon, chalasr, and HeahDude reacted with laugh emoji

@chalasr
Copy link
MemberAuthor

@lsmith77 A great precursor :)

fabpot added a commit that referenced this pull requestFeb 28, 2017
…ocators (nicolas-grekas, chalasr)This PR was merged into the 3.3-dev branch.Discussion----------Remove some container injections in favor of service locators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets |#21553 (comment)| License       | MIT| Doc PR        | n/aCommits-------8293b75 Replace some container injections by service locators0be9ea8 [EventDispatcher] Fix abstract event subscribers registration
symfony-splitter pushed a commit to symfony/event-dispatcher that referenced this pull requestFeb 28, 2017
…ocators (nicolas-grekas, chalasr)This PR was merged into the 3.3-dev branch.Discussion----------Remove some container injections in favor of service locators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets |symfony/symfony#21553 (comment)| License       | MIT| Doc PR        | n/aCommits-------8293b753cf Replace some container injections by service locators0be9ea8ba1 [EventDispatcher] Fix abstract event subscribers registration
symfony-splitter pushed a commit to symfony/http-kernel that referenced this pull requestFeb 28, 2017
…ocators (nicolas-grekas, chalasr)This PR was merged into the 3.3-dev branch.Discussion----------Remove some container injections in favor of service locators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets |symfony/symfony#21553 (comment)| License       | MIT| Doc PR        | n/aCommits-------8293b75 Replace some container injections by service locators0be9ea8 [EventDispatcher] Fix abstract event subscribers registration
symfony-splitter pushed a commit to symfony/framework-bundle that referenced this pull requestFeb 28, 2017
…ocators (nicolas-grekas, chalasr)This PR was merged into the 3.3-dev branch.Discussion----------Remove some container injections in favor of service locators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets |symfony/symfony#21553 (comment)| License       | MIT| Doc PR        | n/aCommits-------8293b753cf Replace some container injections by service locators0be9ea8ba1 [EventDispatcher] Fix abstract event subscribers registration
@nicolas-grekasnicolas-grekas modified the milestones:3.x,3.3Mar 24, 2017
@fabpotfabpot mentioned this pull requestMay 1, 2017
javiereguiluz added a commit to symfony/symfony-docs that referenced this pull requestMay 4, 2017
…guiluz)This PR was merged into the master branch.Discussion----------[DI] Add section about service locatorsAdds documentation forsymfony/symfony#21553 andsymfony/symfony#22024.Any suggestion will be much appreciated, as usual.Commits-------fa19770 Fix service locator declarationf5e4942 Rewords5efacd0 [DI] Add section about Service Locators
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@fabpotfabpotfabpot left review comments

@nicolas-grekasnicolas-grekasnicolas-grekas approved these changes

@stofstofstof requested changes

+5 more reviewers

@TaluuTaluuTaluu left review comments

@jvasseurjvasseurjvasseur left review comments

@unkindunkindunkind left review comments

@ogizanagiogizanagiogizanagi left review comments

@GuilhemNGuilhemNGuilhemN 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.

11 participants

@chalasr@ogizanagi@jvasseur@stof@nicolas-grekas@fabpot@lsmith77@Taluu@unkind@GuilhemN@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp