Thanks David for such a comprehensive roundup of what's being proposed. I 
personally found the time and effort you put into explaining the problem a 
great help.

I am against adding this functionality for reasons already stated - I 
believe it's out of the scope of this PSR and would be an implementation 
detail rather than an actual part of the standard. With that said, I am 
only slightly against it, we do something similar in Zend\ServiceManager 
where we allow people to travel up to the composite container (although 
it's not really a composite container as it can contain services) so I'm 
very interested in possibly using the pattern you explained in place of 
this. 

I'm not convinced it should be in the spec, but would happily concede if 
others were all in favour.

G

On Thursday, 17 November 2016 06:29:31 UTC-5, David Négrier wrote:
>
> Ok folks!
>
> I've been delaying this for a while to give some room for the rest of the 
> questions about PSR-11, but now is the time to dwelve into the details of 
> the delegate lookup feature :)
>
> I know some of you have doubts about this part of the PSR, so the first 
> thing might be to decide whether we want to keep it or not. If we decide to 
> keep it, I'm sure there are plenty of things we can do to improve the 
> wording :)
>
> (post is quite long and contains images. If it does not display well, I 
> cross-posted 
> here 
> <https://www.thecodingmachine.com/psr-11-an-in-depth-view-at-the-delegate-lookup-feature/>
> )
>
> *What is it?*
>
> The *delegate dependency lookup feature* (let's call it DDL) is a *design 
> pattern.*
> It is the only way we have found to compose containers.
>
> *What problem does it solve?*
>
> Let's admit you want to compose/chain containers (more on why you might 
> want to do this later).
> To do this, you would typically use a "CompositeContainer 
> <https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php>"
>  
> (a container without any entry whose role is to ask each "child" container 
> in turn "do you contain the entry I'm looking for?")
>
> [image: composite.png]
>
> The CompositeContainer is not enough. Let's admit container 1 contains 
> your controller. You fetch your controller. Your controller has a 
> dependency on your ORM's entity manager. If the entity manager is part of 
> container 2, container 1 will be unable to fetch it (because internally, it 
> will perform a call "$this->get()" to fetch the entityManager dependency. 
> Similarly, the entityManager should be able to fetch a dependency (for 
> instance the dbConnection) inside another container...
>
> The delegate lookup feature simply states that containers should not fetch 
> their dependencies locally, but instead, they should fetch their 
> dependencies from the top-most container (in this example, this is the 
> CompositeContainer).
>
> In our example, this would go like this:
>
> - The framework asks for the "controller" entry to the CompositeContainer 
> - The CompositeContainer asks Container 1: "do you have "Controller". 
> Container 1 answers yes
> - The CompositeContainer asks Container 1: "give me "Controller".
> - Container 1 needs to fetch the "EntityManager" dependency, so Container 
> 1 calls *$compositeContainer->get('EntityManager')*; (here! container 1 
> is "delegating" the dependency lookup to the composite container)
> - The CompositeContainer asks Container 1: "do you have "EntityManager"? 
> => response is no.
> - The CompositeContainer asks Container 2: "do you have "EntityManager"? 
> => response is yes. The CompositeContainer asks Container 2: "give me 
> "EntityManager".
> - ... and do on
>
>
> *Do we want this?*
>
> To be honest, PSR-11 is already useful without the DDL feature. The 
> ContainerInterface is enough to allow users to swap container 
> implementations.
>
> Being able to compose containers is a nice feature, but it's optional. 
> PSR-11 can be useful without DDL.
>
> *What are valid use cases for this?*
>
> First of all, I do not expect major full-stack frameworks to use this. It 
> is obvious from the example above that there is a performance hit when 
> using a composite container (you have to ask each container in turn whether 
> it contains the instance you are looking for or not).
>
> Frameworks very focused on performance (like Symfony full-stack) should 
> stay away from CompositeContainers (more on performance below).
>
> Yet, here are a few use cases where composite containers are valuable:
>
> 1- Migration!
>
> Let's admit you started a small app with Slim3 and Pimple. Your app is 
> getting bigger. You now have more than 200 services declared in Pimple and 
> you want to migrate away to something more powerful (maybe you want to 
> benefit from Autowiring or maybe you need lazy services for performance 
> issues...)
> The DDL feature allows you to put Pimple side-by-side with your new 
> container of choice and migrate entries slowly, one by one. This is really 
> cool because it makes a daunting task a lot easier.
>
> 2- I don't care about performance, give me features!
>
> Running containers side-by-side is a great away to enhance a container 
> with the features of another container. For instance, you could enhance 
> your existing container with a sidekick containers dedicated to creating 
> aliases or serving a "lazy" version of your services, etc...
>
> Not everybody cares for container performance. For instance, if you are 
> doing async PHP (with ReactPHP or another lib), container performance is 
> not a concern at all (since services are created once at the beginning of 
> the script and are reused across requests).
>
> *By the way, what is the real performance impact of using a 
> CompositeContainer and DDL?*
>
> I've been doing some tests using a modified version of Symfony.
> You can read my old article about the performance impact on DDL here 
> <https://www.thecodingmachine.com/psr-11-performance-impact-of-the-delegate-lookup-feature/>.
>  
> Spoiler alert: impact is quite low, I was not able to measure it.
>
>
> *How do we implement this?*
>
> DDL support is quite easy to add in any container out there. From my 
> experience with container-interop, I've never seen a case where it took 
> more than a few lines of code to add support.
>
> Typical implementation goes like this:
>
> 1- You modify the constructor to accept an additional parameter: the root 
> container. This parameter is optional. If not passed, the root container is 
> the container itself.
>
> So your container constructor now looks like this:
>
> public function __construct($param1, $param2, ..., ContainerInterface 
> $rootContainer = null) {
>     ...
>     $this->rootContainer = $rootContainer ?: $this;
> }
>
> In your container code, when you perform a call to get on a dependency, 
> instead of calling $this->get, you call $this->rootContainer->get.
>
> Aaaaaand you're done :)
>
> *Important*: As you can see, if you are not using a composite container, 
> the impact on performance of a container is null (it is almost the same 
> code executed). So a container can add support for DDL without impacting 
> its average performance.
>
> *Is it used somewhere?*
>
> The ContainerInterface has been mostly designed by looking at what common 
> methods were supported by containers out there.
> On the contrary, the dependency delegate lookup design pattern has been 
> "invented" by container-interop and does not stem from previous work from 
> one container or another. This is of course to be expected, because without 
> the ContainerInterface, it is not possible to envision composing containers.
>
> The delegate dependency lookup feature is already supported by a number of 
> containers out there (see: 
> https://github.com/container-interop/container-interop#projects-implementing-the-delegate-lookup-feature
>  )
>
> I don't know if it is wildly used or not, but I know at least one place 
> where this was useful to me: the laravel <=> 
> container-interop/service-provider bridge 
> <https://github.com/thecodingmachine/laravel-universal-service-provider/>. 
> I used it here to add support for container-interop's service providers 
> <https://github.com/container-interop/service-provider> into Laravel. 
> Rather than forking Laravel container to add support for service providers 
> in it, I decided to add another container (Simplex) that already had 
> support for service providers next to Laravel's one.
>
>
> *Summary:*
>
> Is it essential to PSR-11? No
> Is it useful? Yes, in some specific cases (I do not expect it to be wildly 
> used)
> Is it easy to implement? Dead easy
> Does it have an impact on performance? No if only one container is used 
> (business as usual), yes if many containers are used (but this is to be 
> expected)
>
> Should it be part of the PSR?
>
> My personal opinion is yes. It does not hurt, it is optional, it is the 
> only way we could put containers side-by-side and let them share entries. I 
> believe advertising this design pattern in the PSR will make it more wildly 
> adopted. This, in turn, will increase framework interoperability.
>
> Note: there are a ton of things I would still like to discuss, like "can a 
> composite container contain entries?" or "can I nest composite 
> containers?". Also, @crell asked a lot of questions in his initial review 
> and I'm only surfacing a few answers but I'm afraid that will get too 
> tricky quickly, so I'm going to stop there for the time being. :)
>
> But here is the real question: do you think DDL is useful, and do you 
> think it should be part of PSR-11?
>
> Best regards,
> David.
>

-- 
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to php-fig+unsubscr...@googlegroups.com.
To post to this group, send email to php-fig@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/273f0df4-ee6b-4712-b550-4609fb515ec6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to