> I think it was a mistake that PSR-11 allowed the container to contain 
> scalar values. 
>

I strongly disagree here. I think the decision we took to allow returning 
anything was the good one.
Otherwise, containers like Pimple based on factories could never properly 
implement PSR-11. PSR-11 adapted fairly well to the wide range of 
containers out there, without dictating a philosophy. If I had to start it 
again (god help me :), I would do the same!
 

>
> The value in a config PSR is that it would greatly improve type safety 
> (container contains objects, config contains scalars) and therefore 
> make it much easier to approach how a service provider is configured, 
> as not all containers have a way to fetch scalar values. 
>

Isn't this "type safety" an illusion? I mean... a config PSR could still 
return strings, ints, arrays of scalars, ...
And even restricted to only returning objects, a container can return ANY 
type of object on a given key.

So spliting one container into 2 containers (one for config and one for 
objects) is very far from bringing us any real type safety.

If we want to address type safety issues, we'd better look at Symfony's 
proposal (using an array to map identifiers to types). That brings way more 
value.

++
David
Twitter: @david_negrier
Github: @moufmouf


 

> -- 
> Woody Gilk 
> http://about.me/shadowhand 
>
>
> On Mon, Mar 5, 2018 at 8:54 AM, David Négrier <david....@gmail.com 
> <javascript:>> wrote: 
> > Hey Woody, 
> > 
> > I'm really not sure we need that config PSR. 
> > 
> > My reasoning goes like this: 
> > 
> > What do we need for a config PSR? We cannot standardize a configuration 
> > format. Each framework has its own format (YAML, PHP array files, NEON, 
> > etc...) 
> > 
> > However, we can standardize how we "get" a config value from the 
> > application. 
> > 
> > So we would certainly have an interface like a `ConfigFetcherInterface` 
> with 
> > a "get" (and maybe a "has" method): 
> > 
> > interface ConfigFetcherInterface 
> > { 
> >     public function get(string $name) : mixed; 
> >     public function has(string $name) : mixed; 
> > } 
> > 
> > Whoops... It is identical to PSR-11! 
> > You can argue it serves a different purpose, but PSR-11 already states 
> that 
> > a container can contain anything (including scalars like strings, ints, 
> > etc...) 
> > So we could use PSR-11 for configuration fetching. 
> > 
> > We could however discuss if we want to split configuration from services 
> in 
> > the function signature of factories. 
> > 
> > Current signature for a factory is: 
> > 
> > function(ContainerInterface $container) 
> > 
> > where $container contains both configuration and services. 
> > 
> > We could change the signature to something like: 
> > 
> > function(ContainerInterface $configContainer, ContainerInterface 
> > $serviceContainer) 
> > 
> > I'm not really a big fan of this, but I can understand it rings a bell 
> for 
> > people we are making a clear distinction between config and services 
> (like 
> > in Symfony). 
> > 
> > What do you think? 
> > 
> > David. 
> > 
> > 
> > 
> > Le dim. 4 mars 2018 à 01:44, Woody Gilk <woody...@gmail.com 
> <javascript:>> a écrit : 
> >> 
> >> I think that Oscar's PR proves the point that we need a config PSR 
> before 
> >> service providers will make sense. Using the container for 
> configuration is 
> >> not (imho) the purpose of containers and makes it harder to standardize 
> on 
> >> using FQCN for container identifiers. 
> >> 
> >> 
> >> On Sat, Mar 3, 2018, 15:18 Oscar Otero <oscar...@gmail.com 
> <javascript:>> wrote: 
> >>> 
> >>> To me, the problem with these proposals is they have different 
> >>> responsabilities in the same class. A ServiceProvider implementation 
> >>> includes the callables to create instances of different services, 
> defines 
> >>> the key used to store each of these services and even include two 
> methods: 
> >>> one to get factories and other for extensions, so in many times you 
> have to 
> >>> create a method returning an empty array (see 
> >>> https://github.com/container-interop/service-provider/issues/43), 
> (Interface 
> >>> segregation principle). 
> >>> 
> >>> I proposed a different approach 
> >>> (https://github.com/container-interop/service-provider/issues/45) 
> consisting 
> >>> in creating a factory for each service. The proposal is just a sketch 
> to 
> >>> illustrate the main concept (each object has just one responsabiity: 
> create 
> >>> a service) and surely can be improved. 
> >>> 
> >>> 
> >>> 
> >>> El 3 mar 2018, a las 14:07, David Négrier <david....@gmail.com 
> <javascript:>> 
> >>> escribió: 
> >>> 
> >>> To build upon Larry's answer, the container does not only contains 
> >>> services. It knows how to build them. 
> >>> 
> >>> If we ended up "setting" in a container every service, well first... 
> we 
> >>> could use arrays instead of containers :) and then, the performance of 
> the 
> >>> container would degrade proportionally to the number of entries in the 
> >>> container. You would have to instantiate and store every service on 
> every 
> >>> request, even if the service does not end up being called. So 
> basically, the 
> >>> bigest your application, the slowest. 
> >>> 
> >>> This is why both proposals carefully avoid the use of a "set" method. 
> >>> 
> >>> ++ 
> >>> David. 
> >>> 
> >>> 
> >>> Le vendredi 2 mars 2018 23:02:49 UTC+1, Larry Garfield a écrit : 
> >>>> 
> >>>> The reason a simple set() won't work is that the "thing" put into the 
> >>>> container is generally not a value but instructions for how to 
> produce a 
> >>>> value 
> >>>> on-demand, and the value is typically a service object.  How to 
> encode 
> >>>> "here's 
> >>>> how to build the thing" is the main question to answer. 
> >>>> 
> >>>> --Larry Garfield 
> >>>> 
> >>>> On Friday, March 2, 2018 1:18:32 PM CST David Lundgren wrote: 
> >>>> > If the problem to solve is "what's a common way to put things in a 
> >>>> > container?" wouldn't the simplest solution be a `set($id, $value)` 
> >>>> > method 
> >>>> > on the container? 
> >>>> > 
> >>>> > Most container implementations already have a method of this sort. 
> >>>> > While a 
> >>>> > few have shared/concrete/protected concepts baked in, they could 
> make 
> >>>> > separate methods for changing it  based on the $id. 
> >>>> > 
> >>>> > Dave 
> >>>> > 
> >>>> > On Thursday, March 1, 2018 at 11:16:29 AM UTC-6, David Négrier 
> wrote: 
> >>>> > > Hey list, 
> >>>> > > 
> >>>> > > We are still in the process of forming a working group regarding 
> a 
> >>>> > > Service 
> >>>> > > provider PSR. 
> >>>> > > 
> >>>> > > I've had the chance to speak about this with several Symfony 
> >>>> > > contributors, 
> >>>> > > and while discussing about this idea, Nicolas Grekas 
> >>>> > > <https://github.com/nicolas-grekas/> (from Symfony) came up with 
> an 
> >>>> > > alternative proposal. It's about having many containers working 
> >>>> > > together, 
> >>>> > > with a slightly different scope. First of all, I'd like to thank 
> >>>> > > Nicolas 
> >>>> > > for the time he is investing in researching this issue, and for 
> all 
> >>>> > > the 
> >>>> > > feedback. We talked about his idea with Matthieu Napoli 
> >>>> > > <https://github.com/mnapoli/> and Larry Garfield 
> >>>> > > <https://github.com/crell> at the Paris ForumPHP in November. 
> I'm 
> >>>> > > now 
> >>>> > > sharing this conversation with you. 
> >>>> > > 
> >>>> > > I put this in a blog article that you can find here: 
> >>>> > > 
> >>>> > > 
> https://thecodingmachine.io/psr-11-scope-of-universal-service-providers 
> >>>> > > 
> >>>> > > I'm reposting the content of the article here, since it's 
> directly 
> >>>> > > related 
> >>>> > > to PHP-FIG concerns. It's a bit long, but the topic is worth it 
> :) 
> >>>> > > 
> >>>> > > Stated goal 
> >>>> > > 
> >>>> > > Each framework has it's own custom package format (bundles, 
> >>>> > > packages, 
> >>>> > > modules, etc...). What these package formats are doing is 
> >>>> > > essentially 
> >>>> > > always the same. They are used to put things in a container. 
> >>>> > > 
> >>>> > > If the PHP-FIG could come up with a unique package format that 
> could 
> >>>> > > be 
> >>>> > > supported by all frameworks, package developers could truly write 
> >>>> > > classes 
> >>>> > > that can be used in any framework more easily. 
> >>>> > > 
> >>>> > > Hence, the stated goal of this PSR (let's call it PSR-X since it 
> >>>> > > does not 
> >>>> > > have a number yet) is to find a common way to *put things in a 
> >>>> > > container*. 
> >>>> > > 
> >>>> > > We (the container-interop group) have been working on this for 
> quite 
> >>>> > > some 
> >>>> > > time and have come up with a solution that needs to be turned 
> into a 
> >>>> > > PSR 
> >>>> > > <https://github.com/container-interop/service-provider/>. The 
> idea 
> >>>> > > is to 
> >>>> > > build generic service providers. 
> >>>> > > 
> >>>> > > 
> >>>> > > Current proposal 
> >>>> > > 
> >>>> > > The current proposal is named container-interop/service-provider 
> >>>> > > <https://github.com/container-interop/service-provider/>. In 
> this 
> >>>> > > proposal, we create a ServiceProviderInterface interface that 
> >>>> > > exposes a 
> >>>> > > set of *factories*. 
> >>>> > > 
> >>>> > > 
> >>>> > > class MyServiceProvider implements ServiceProviderInterface{ 
> >>>> > > 
> >>>> > >     public function getFactories() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return [ 
> >>>> > > 
> >>>> > >             'my_service' => function(ContainerInterface 
> $container) 
> >>>> > > : 
> >>>> > >             MyService { 
> >>>> > > 
> >>>> > >                 $dependency = 
> $container->get('my_other_service'); 
> >>>> > >                 return new MyService($dependency); 
> >>>> > > 
> >>>> > >             } 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > >     // ... 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > In the example above, the 'my_service' service can be created by 
> the 
> >>>> > > container by executing the factory (the anonymous function). 
> >>>> > > 
> >>>> > > Additionally, the ServiceProviderInterface let's you *modify* 
> >>>> > > existing 
> >>>> > > services stored in the container. 
> >>>> > > 
> >>>> > > 
> >>>> > > class MyServiceProvider implements ServiceProviderInterface{ 
> >>>> > > 
> >>>> > >     // ... 
> >>>> > > 
> >>>> > >     public function getExtensions() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return [ 
> >>>> > > 
> >>>> > >             Twig_Environment::class => 
> function(ContainerInterface 
> >>>> > >             $container, Twig_Environment $twig) : 
> Twig_Environment 
> >>>> > > {> 
> >>>> > > 
> >>>> > > $twig->addExtension($container->get('my_extension')); 
> >>>> > >                 return $twig; 
> >>>> > > 
> >>>> > >             } 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > In the example above, the service named "Twig_Environment" is 
> >>>> > > modified. We 
> >>>> > > register a new twig extension in it. This is very powerful. This 
> can 
> >>>> > > be 
> >>>> > > used to create arrays and add elements to them, or this can be 
> used 
> >>>> > > to 
> >>>> > > decorate an existing service (using the decorator pattern). 
> Overall, 
> >>>> > > this 
> >>>> > > gives a lot of power to the service provider. 
> >>>> > > 
> >>>> > > Right now, this interface has been tested. It has adapters in 
> >>>> > > Symfony, 
> >>>> > > Laravel, and there is a Pimple fork named Simplex that is also 
> >>>> > > implementing 
> >>>> > > it. You can view the complete list of implementations here 
> >>>> > > 
> >>>> > > <
> https://github.com/container-interop/service-provider#compatible-projects 
> >>>> > > > 
> >>>> > > . 
> >>>> > > 
> >>>> > > 
> >>>> > > The alternative proposal 
> >>>> > > 
> >>>> > > Nicolas Grekas and the Symfony team came up with another proposal 
> >>>> > > <https://github.com/symfony/symfony/pull/25707>. 
> >>>> > > 
> >>>> > > Rather than standardizing service providers, he proposes that 
> each 
> >>>> > > package 
> >>>> > > could provide it's own container. The container would have an 
> >>>> > > interface to 
> >>>> > > expose a list of services to your application's container. 
> >>>> > > 
> >>>> > > The proposal goes like this: 
> >>>> > > 
> >>>> > > 
> >>>> > > interface ServiceProviderInterface extends ContainerInterface{ 
> >>>> > > 
> >>>> > >     /** 
> >>>> > > 
> >>>> > >      * Returns an associative array of service types keyed by 
> names 
> >>>> > >      provided by this object. * 
> >>>> > >      * Examples: 
> >>>> > >      * 
> >>>> > >      *  * array('logger' => 'Psr\Log\LoggerInterface') means the 
> >>>> > > object 
> >>>> > >      provides service implementing Psr\Log\LoggerInterface * 
> >>>> > > under 
> >>>> > >      "logger" name 
> >>>> > >      *  * array('foo' => '?') means that object provides service 
> of 
> >>>> > >      unknown type under 'foo' name *  * array('bar' => 
> '?Bar\Baz') 
> >>>> > > means 
> >>>> > >      that object provides service implementing Bar\Baz or null 
> under 
> >>>> > >      'bar' name * 
> >>>> > >      * @return string[] The provided service types, keyed by 
> service 
> >>>> > > names 
> >>>> > >      */ 
> >>>> > > 
> >>>> > >     public function getProvidedServices(): array; 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > Notice how the ServiceProviderInterface extends the PSR-11 
> >>>> > > ContainerInterface <https://www.php-fig.org/psr/psr-11/>. 
> >>>> > > 
> >>>> > > Here, there is a single function getProvidedServices that 
> provides 
> >>>> > > the 
> >>>> > > names of the provided services as keys, along the type of the 
> >>>> > > service as 
> >>>> > > values. 
> >>>> > > 
> >>>> > > When your application's container is asked for a service that is 
> >>>> > > part of a 
> >>>> > > "service provider", it would simply call the get method of the 
> >>>> > > service 
> >>>> > > provider (since a service provider IS a container) and retrieve 
> the 
> >>>> > > service. 
> >>>> > > 
> >>>> > > There is no way for a service provider to modify services in the 
> >>>> > > application's container (this is a design decision). 
> >>>> > > 
> >>>> > > While talking about this interface, we also mentioned another 
> >>>> > > interface. A 
> >>>> > > service provider can need dependencies stored in another 
> container. 
> >>>> > > It 
> >>>> > > could therefore publish the list of services it is expecting to 
> find 
> >>>> > > in 
> >>>> > > the 
> >>>> > > main container. Therefore, Nicolas proposed an additional 
> interface: 
> >>>> > > ServiceSubscriberInterface, providing a getSubscribedServices 
> >>>> > > method. 
> >>>> > > 
> >>>> > > 
> >>>> > > class TwigContainer implement ServiceProviderInterface, 
> >>>> > > ContainerInterface, ServiceSubscriberInterface {> 
> >>>> > >     //... 
> >>>> > > 
> >>>> > >     public function getSubscribedServices() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         // The TwigContainer needs 2 services to be defined: 
> >>>> > >         //  - "debug" (this is an optionnal bool value) 
> >>>> > >         //  - "twig_extensions" (this is an optionnal array of 
> >>>> > > objects 
> >>>> > >         implementing TwigExtentionInterface) return [ 
> >>>> > > 
> >>>> > >             'debug' => '?bool', 
> >>>> > >             'twig_extensions' => 
> >>>> > > '?'.TwigExtentionInterface::class.'[]', 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > Notice that the 2 interfaces can be considered independently. The 
> >>>> > > ServiceSubscriberInterface allows to add an additional check at 
> >>>> > > container 
> >>>> > > build time (vs getting a runtime exception if a service is 
> lacking a 
> >>>> > > container entry or if the provided container entry is of the 
> wrong 
> >>>> > > type). 
> >>>> > > 
> >>>> > > 
> >>>> > > Comparing of the 2 proposalsRegarding performance 
> >>>> > > 
> >>>> > > Regarding performance, the 2 proposals have very different 
> >>>> > > properties. 
> >>>> > > 
> >>>> > > 
> >>>> > > *In container-interop/service-providers*: 
> >>>> > > 
> >>>> > > The service provider is largely considered as *dumb*. It is *the 
> >>>> > > responsibility of the container* to optimize the calls. 
> >>>> > > 
> >>>> > > Actually, it is possible to get excellent performances if the 
> >>>> > > service 
> >>>> > > provider is providing the factories as public static functions. 
> >>>> > > 
> >>>> > > class MyServiceProvider implements ServiceProviderInterface{ 
> >>>> > > 
> >>>> > >     public function getFactories() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return [ 
> >>>> > > 
> >>>> > >             Twig_Environment::class => [ self::class, 
> 'createTwig' ] 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > >     public static function createTwig(ContainerInterface 
> $container, 
> >>>> > >     Twig_Environment $twig) : Twig_Environment {> 
> >>>> > >         $twig->addExtension($container->get('my_extension')); 
> >>>> > >         return $twig; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > >     // ... 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > In this case, a compiled container could directly call the 
> factory, 
> >>>> > > without having to instantiate the service provider class nor call 
> >>>> > > the 
> >>>> > > getFactories method. This is definitely the best performance you 
> can 
> >>>> > > get 
> >>>> > > (but is still to the good-will of the service-provider author 
> that 
> >>>> > > must 
> >>>> > > use public static methods instead of closures). 
> >>>> > > 
> >>>> > > 
> >>>> > > *In Symfony's proposal*: 
> >>>> > > 
> >>>> > > The service provider is an actual container. *The service 
> provider 
> >>>> > > is 
> >>>> > > therefore in charge of the performance of delivered services*. 
> >>>> > > 
> >>>> > > It probably cannot beat the direct call to a public static 
> function 
> >>>> > > (since you have to call at least the service provider constructor 
> >>>> > > and the 
> >>>> > > get function of the service provider), but can still be quite 
> >>>> > > optimized. 
> >>>> > > The important part is that the performance is delegated to the 
> >>>> > > service 
> >>>> > > provider. 
> >>>> > > 
> >>>> > > Dealing with service names 
> >>>> > > 
> >>>> > > *In container-interop/service-providers*: 
> >>>> > > 
> >>>> > > The idea is that service providers should respect some kind of 
> >>>> > > convention. 
> >>>> > > 
> >>>> > > If you are writing a service provider for Monolog, the service 
> >>>> > > creating 
> >>>> > > the Monolog\Logger class should be named Monolog\Logger. This 
> will 
> >>>> > > allow 
> >>>> > > containers using *auto-wiring* to automatically find the service. 
> >>>> > > 
> >>>> > > Additionally, you can create an *alias* for your service on the 
> >>>> > > Psr\Log\LoggerInterface, if you want to auto-wire the 
> >>>> > > LoggerInterface to 
> >>>> > > the Monolog\Logger service. 
> >>>> > > 
> >>>> > > The code would therefore look like this: 
> >>>> > > 
> >>>> > > 
> >>>> > > class MonologServiceProvider implements ServiceProviderInterface{ 
> >>>> > > 
> >>>> > >     public function getFactories() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return [ 
> >>>> > > 
> >>>> > >             \Psr\Log\LoggerInterface::class => [ self::class, 
> >>>> > >             'createAlias' ], 
> >>>> > >             \Monolog\Logger::class => [ self::class, 
> 'createLogger' 
> >>>> > > ], 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > >     public static function createLogger(): \Monolog\Logger 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return new \Monolog\Logger('default'); 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > >     public static function createAlias(ContainerInterface 
> >>>> > > $container): 
> >>>> > >     \Monolog\Logger { 
> >>>> > > 
> >>>> > >         return $container->get('\Monolog\Logger'); 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > >     // ... 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > *In Symfony's proposal*: 
> >>>> > > 
> >>>> > > I must admit I'm not 100% clear on Nicolas thought here. There 
> are 
> >>>> > > really 
> >>>> > > 2 solutions. Either we adopt a convention (just like with 
> >>>> > > container-interop/service-provider), either we can decide that 
> the 
> >>>> > > container can be "clever". After all, using the 
> getProvidedServices 
> >>>> > > class, a container can know the type of all provided services, so 
> if 
> >>>> > > it 
> >>>> > > could decide to autowire them by its own. 
> >>>> > > 
> >>>> > > For instance, if a call to getProvidedServices returns: 
> >>>> > > 
> >>>> > > [ 
> >>>> > > 
> >>>> > >     'logger' => '\Monolog\Logger' 
> >>>> > > 
> >>>> > > ] 
> >>>> > > 
> >>>> > > the container could decide on its own that the 'logger' service 
> is a 
> >>>> > > good 
> >>>> > > fit to auto-wire '\Monolog\Logger'. 
> >>>> > > 
> >>>> > > At this stage, the decision is delegated to the container. The 
> >>>> > > service 
> >>>> > > provider is more "dumb". It does not know and does not decide 
> what 
> >>>> > > gets 
> >>>> > > auto-wired. The container does (this means there is probably some 
> >>>> > > configuration required in the container). 
> >>>> > > 
> >>>> > > Dealing with list of services 
> >>>> > > 
> >>>> > > It is pretty common to want to add a service to a list of 
> services. 
> >>>> > > In 
> >>>> > > containers, this is usually done by using "tags". None of the 2 
> >>>> > > proposals 
> >>>> > > supports the notion of tags directly. But both have workarounds. 
> >>>> > > 
> >>>> > > 
> >>>> > > *In container-interop/service-providers*: 
> >>>> > > 
> >>>> > > The idea is to create an entry in the container that is actually 
> an 
> >>>> > > array 
> >>>> > > of services. Each service provider can then modify the array to 
> >>>> > > register 
> >>>> > > its own service in it. 
> >>>> > > 
> >>>> > > class MonologHandlerServiceProvider implements 
> >>>> > > ServiceProviderInterface{ 
> >>>> > > 
> >>>> > >     // ... 
> >>>> > > 
> >>>> > >     public function getExtensions() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return [ 
> >>>> > > 
> >>>> > >             HandlerInterface::class.'[]' => 
> >>>> > > function(ContainerInterface 
> >>>> > >             $container, array $handlers = []) : array {> 
> >>>> > >                 $handlers[] = new MyMonologHandler(); 
> >>>> > >                 return $handlers; 
> >>>> > > 
> >>>> > >             } 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > *In Symfony's proposal*: 
> >>>> > > 
> >>>> > > The PR does not state it, but we could imagine allowing types 
> with 
> >>>> > > '[]' at 
> >>>> > > the end. 
> >>>> > > 
> >>>> > > For instance, if a call to getProvidedServices returns: 
> >>>> > > 
> >>>> > > [ 
> >>>> > > 
> >>>> > >     'monologHandlers' => HandlerInterface::class.'[]' 
> >>>> > > 
> >>>> > > ] 
> >>>> > > 
> >>>> > > then the container might decide to automatically append the 
> services 
> >>>> > > returned by 'monologHandlers' to services with the same name in 
> the 
> >>>> > > main 
> >>>> > > container. 
> >>>> > > 
> >>>> > > Said otherwise, the container calls get('monologHandlers') on all 
> >>>> > > the 
> >>>> > > service providers and concatenates those. 
> >>>> > > 
> >>>> > > Dealing with list of services with priorities 
> >>>> > > 
> >>>> > > Sometimes, you are adding a service in a list that must be 
> ordered. 
> >>>> > > 
> >>>> > > Let's take an example. You just wrote a PSR-15 middleware that is 
> an 
> >>>> > > error 
> >>>> > > handler (like the Whoops middleware 
> >>>> > > <https://github.com/middlewares/whoops>). This middleware must 
> >>>> > > absolutely 
> >>>> > > be the first to be executed in the list of middlewares (because 
> it 
> >>>> > > will 
> >>>> > > catch any exception that might be thrown by other middlewares). 
> >>>> > > 
> >>>> > > Some containers allow to tag with priorities. But we don't have 
> this 
> >>>> > > notion in our interfaces. 
> >>>> > > 
> >>>> > > How can we deal with that? 
> >>>> > > Do we need this? Discussing with Matthieu Napoli, I know that 
> >>>> > > Matthieu 
> >>>> > > thinks this can be out of scope of the PSR. In Matthieu's view, 
> it 
> >>>> > > is not 
> >>>> > > the responsibility of the service provider to decide where a 
> service 
> >>>> > > is 
> >>>> > > inserted in a list. I personnally feel this is quite an important 
> >>>> > > feature. 
> >>>> > > An error handling middleware knows it must be at the very 
> beginning 
> >>>> > > so I 
> >>>> > > think we (the service providers authors) should do all what we 
> can 
> >>>> > > to help 
> >>>> > > the developer using our middleware to put it at the right spot. 
> For 
> >>>> > > the 
> >>>> > > author of the Whoops middleware service provider, it is quite 
> >>>> > > obvious that 
> >>>> > > the middleware must go first. For the average PHP developer that 
> is 
> >>>> > > not an 
> >>>> > > expert in middleware architectures, it might be far less obvious. 
> >>>> > > 
> >>>> > > 
> >>>> > > *In container-interop/service-providers*: 
> >>>> > > 
> >>>> > > The idea is to create an entry in the container that is a 
> priority 
> >>>> > > queue. 
> >>>> > > For instance, PHP has the great \SplPriorityQueue. 
> >>>> > > 
> >>>> > > class WhoopsMiddlewareServiceProvider implements 
> >>>> > > ServiceProviderInterface{ 
> >>>> > > 
> >>>> > >     // ... 
> >>>> > > 
> >>>> > >     public function getExtensions() 
> >>>> > >     { 
> >>>> > > 
> >>>> > >         return [ 
> >>>> > > 
> >>>> > >             'middlewareList' => function(ContainerInterface 
> >>>> > > $container, 
> >>>> > >             \SplPriorityQueue $middlewares) : \SplPriorityQueue 
> {> 
> >>>> > >                 $middlewares->insert(new WhoopsMiddleware(), 
> -9999); 
> >>>> > >                 // Note: we should replace the -9999 by a 
> constant 
> >>>> > > like 
> >>>> > >                 MiddlewarePriorities::VERY_EARLY return 
> >>>> > > $middlewares; 
> >>>> > > 
> >>>> > >             } 
> >>>> > > 
> >>>> > >         ]; 
> >>>> > > 
> >>>> > >     } 
> >>>> > > 
> >>>> > > } 
> >>>> > > 
> >>>> > > 
> >>>> > > *In Symfony's proposal*: 
> >>>> > > 
> >>>> > > How to deal with this in Symfony's proposal is quite unclear to 
> me. 
> >>>> > > 
> >>>> > > We could decide this is out of scope. 
> >>>> > > 
> >>>> > > We could also decide that we have many unsorted list, like 
> >>>> > > 'earlyMiddlewares', 'utilityMiddlewares', 'routerMiddlewares'... 
> >>>> > > that are 
> >>>> > > concatenated by the middleware service provider and fed to the 
> >>>> > > middleware 
> >>>> > > pipe. 
> >>>> > > 
> >>>> > > Miscellaneous 1: introspection 
> >>>> > > 
> >>>> > > Symfony's proposal has 2 wonderful features that 
> >>>> > > container-interop/service-provider does not have. They are not 
> >>>> > > directly 
> >>>> > > 
> >>>> > > necessary to our stated goal, but are quite nice: 
> >>>> > >    - the ServiceProviderInterface is actually an introspection 
> >>>> > > interface 
> >>>> > >    into any container implementing it. This gives us a lot of 
> room 
> >>>> > > to 
> >>>> > >    write 
> >>>> > >    cross-framework tools that can scan containers and analyze 
> them. 
> >>>> > > Pretty 
> >>>> > >    cool. 
> >>>> > >    - the fact that a service provider can publish the list of 
> >>>> > >    dependencies it needs (the ServiceSubscriberInterface) is in 
> my 
> >>>> > >    opinion a very good idea. A service provider offers some 
> entries 
> >>>> > > but 
> >>>> > >    can 
> >>>> > > 
> >>>> > >    also require some entries. By publishing its requirements, we 
> >>>> > > get: 
> >>>> > >       - automated documentation 
> >>>> > >       - the possibility to do static analysis 
> >>>> > >       - the possibility to write tool chains that help the 
> developer 
> >>>> > > set 
> >>>> > >       up service providers (think about a huge online database of 
> >>>> > > all 
> >>>> > >       service 
> >>>> > >       providers available on Packagist with what they offer and 
> what 
> >>>> > > they 
> >>>> > >       require 
> >>>> > > 
> >>>> > >       :) ) 
> >>>> > > 
> >>>> > > Miscellaneous 2: factory services 
> >>>> > > 
> >>>> > > PSR-11 recommends that 2 successive calls to get should return 
> the 
> >>>> > > same 
> >>>> > > entry: 
> >>>> > > 
> >>>> > > Two successive calls to get with the same identifier SHOULD 
> return 
> >>>> > > the 
> >>>> > > same value. 
> >>>> > > 
> >>>> > > Indeed, a container contains services. It should not act as a 
> >>>> > > factory. 
> >>>> > > Yet, it does not forbid containers to act as a factory (we used 
> >>>> > > "SHOULD" 
> >>>> > > and not "MUST" in PSR-11). *container-interop/service-provider* 
> on 
> >>>> > > the 
> >>>> > > other end is very explicit. The service provider provides 
> factories, 
> >>>> > > and 
> >>>> > > the container MUST cache the provided service. So for services 
> >>>> > > provided by 
> >>>> > > *container-interop/service-provider*, 2 successive calls to the 
> >>>> > > container 
> >>>> > > MUST return the same object. I don't see this as a problem, 
> rather 
> >>>> > > as a 
> >>>> > > feature. Yet, with Symfony's proposal, since calls to "get" are 
> >>>> > > delegated 
> >>>> > > to the service provider (that is a container itself), we could 
> write 
> >>>> > > a 
> >>>> > > service provider that provides a new service on each call to get. 
> >>>> > > Symfony's 
> >>>> > > proposal is more flexible in that regard. 
> >>>> > > 
> >>>> > > Summary / TL;DR 
> >>>> > > 
> >>>> > > That table below summarizes the differences between the 2 
> proposals: 
> >>>> > > 
> >>>> > > 
> >>>> > > 
> >>>> > > *container-interop* *Symfony* 
> >>>> > > Performance Container is in charge Service provider is in charge 
> >>>> > > Service names By convention Can be deduced from types 
> >>>> > > Static analysis No Possible 
> >>>> > > Modifying services Yes (powerful service providers) No (dumb 
> service 
> >>>> > > providers) 
> >>>> > > Tagged services Yes, via modified arrays Yes 
> >>>> > > Tagged services with priorities Yes, via modified 
> SplPriorityQueues 
> >>>> > > No 
> >>>> > > (out of scope?) 
> >>>> > > 
> >>>> > > 
> >>>> > > My thoughts 
> >>>> > > This section highlights my current opinions. Others might 
> completely 
> >>>> > > disagree and I think it is important we have a discussion about 
> what 
> >>>> > > we 
> >>>> > > want to achieve. 
> >>>> > > 
> >>>> > > By standardizing service providers, we are shifting the 
> >>>> > > responsibility of 
> >>>> > > writing the "glue code" from the framework developer to the 
> package 
> >>>> > > developer. For instance, if you consider Doctrine ORM, it is 
> likely 
> >>>> > > that 
> >>>> > > the Doctrine service provider would be written by the Doctrine 
> >>>> > > authors 
> >>>> > > (rather than the Symfony/Zend developers). It is therefore in my 
> >>>> > > opinion 
> >>>> > > important to empower the package developer with an interface that 
> >>>> > > gives 
> >>>> > > him/her some control over what gets stored in the container. 
> >>>> > > 
> >>>> > > 
> >>>> > > Existing packaging systems (like Symfony bundles or Laravel 
> service 
> >>>> > > providers) have already this capability and I believe we should 
> aim 
> >>>> > > for 
> >>>> > > this in the PSR. 
> >>>> > > 
> >>>> > > 
> >>>> > > Taking the "PSR-15 Whoops middleware" example, it is for me very 
> >>>> > > important 
> >>>> > > that the service provider author can decide where in the 
> middleware 
> >>>> > > pipe 
> >>>> > > the middleware is added. This means being able to add a service 
> at a 
> >>>> > > given 
> >>>> > > position in a list (or having tags with priorities). This, in my 
> >>>> > > opinion, 
> >>>> > > should be in the scope of the PSR. 
> >>>> > > 
> >>>> > > Said otherwise, while registering the service provider in the 
> >>>> > > container, 
> >>>> > > the user should be able to write: 
> >>>> > > 
> >>>> > > 
> >>>> > > $container->register(new WhoopsMiddlewareServiceProvider()); 
> >>>> > > 
> >>>> > > 
> >>>> > > instead of something like: 
> >>>> > > 
> >>>> > > 
> >>>> > > $container->register(new WhoopsMiddlewareServiceProvider(), [ 
> >>>> > > 
> >>>> > >     'priority' => [ 
> >>>> > > 
> >>>> > >         WhoopsMiddleware::class => -999 
> >>>> > > 
> >>>> > >     ] 
> >>>> > > 
> >>>> > > ]); 
> >>>> > > 
> >>>> > > 
> >>>> > > In this regard, I feel the *container-interop/service-provider* 
> >>>> > > proposal 
> >>>> > > is better suited (because it allows to modify an existing service 
> >>>> > > and that 
> >>>> > > is all we need). 
> >>>> > > 
> >>>> > > That being said, the proposal of Nicolas has plenty of advantages 
> I 
> >>>> > > can 
> >>>> > > 
> >>>> > > also very well see: 
> >>>> > >    - container introspection 
> >>>> > >    - better maintainability/documentation through better tooling 
> >>>> > > 
> >>>> > > I have a gut feeling that there is something that can be done to 
> >>>> > > merge the 
> >>>> > > 2 proposals and get the best of both worlds. Or maybe we can have 
> >>>> > > the 2 
> >>>> > > proposals live side by side (one for service providers and the 
> other 
> >>>> > > for 
> >>>> > > container introspection?) 
> >>>> > > 
> >>>> > > 
> >>>> > > What do you think? 
> >>>> > > 
> >>>> > > What should be the scope of the PSR? 
> >>>> > > 
> >>>> > > For you, is it important to give service provider some control 
> over 
> >>>> > > the 
> >>>> > > container or should they be "dumb" and just provide instances 
> (with 
> >>>> > > the 
> >>>> > > controller keeping the control on how the instances are managed)? 
> >>>> > > 
> >>>> > > 
> >>>> > > ++ 
> >>>> > > 
> >>>> > > David 
> >>>> > > 
> >>>> > > Twitter: @david_negrier 
> >>>> > > 
> >>>> > > Github: @moufmouf 
> >>>> 
> >>> 
> >>> -- 
> >>> 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+u...@googlegroups.com <javascript:>. 
> >>> To post to this group, send email to php...@googlegroups.com 
> <javascript:>. 
> >>> To view this discussion on the web visit 
> >>> 
> https://groups.google.com/d/msgid/php-fig/89c8e62f-b72f-459d-995f-cf0e365ff13c%40googlegroups.com.
>  
>
> >>> For more options, visit https://groups.google.com/d/optout. 
> >>> 
> >>> 
> >>> -- 
> >>> 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+u...@googlegroups.com <javascript:>. 
> >>> To post to this group, send email to php...@googlegroups.com 
> <javascript:>. 
> >>> 
> >>> To view this discussion on the web visit 
> >>> 
> https://groups.google.com/d/msgid/php-fig/5329ADE7-B35F-45B6-A336-C2D4BCCC529F%40gmail.com.
>  
>
> >>> For more options, visit https://groups.google.com/d/optout. 
> >> 
> >> -- 
> >> 
> >> Woody Gilk 
> >> http://about.me/shadowhand 
> >> 
> >> -- 
> >> You received this message because you are subscribed to a topic in the 
> >> Google Groups "PHP Framework Interoperability Group" group. 
> >> To unsubscribe from this topic, visit 
> >> https://groups.google.com/d/topic/php-fig/Up0JATOb0-w/unsubscribe. 
> >> To unsubscribe from this group and all its topics, send an email to 
> >> php-fig+u...@googlegroups.com <javascript:>. 
> >> To post to this group, send email to php...@googlegroups.com 
> <javascript:>. 
> >> To view this discussion on the web visit 
> >> 
> https://groups.google.com/d/msgid/php-fig/CAGOJM6%2B7z2R21UUGM675Bv%2B_JnX%3DSbu2wFc6PSECvK8_FsYGJA%40mail.gmail.com.
>  
>
> >> For more options, visit https://groups.google.com/d/optout. 
> > 
> > -- 
> > 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+u...@googlegroups.com <javascript:>. 
> > To post to this group, send email to php...@googlegroups.com 
> <javascript:>. 
> > To view this discussion on the web visit 
> > 
> https://groups.google.com/d/msgid/php-fig/CABAasbeaw5MXGBWJ1vLnqXUP9z1uXsKrDeo-3-_-ritgdQsOgQ%40mail.gmail.com.
>  
>
> > 
> > For more options, visit https://groups.google.com/d/optout. 
>

-- 
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/5edaf17b-756b-4a61-aa1d-765f13af8c20%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to