PSR for DI container service definitions

582 views
Skip to first unread message

Bernhard Schussek

unread,
Jul 29, 2015, 12:51:53 PM7/29/15
to php...@googlegroups.com
Hi all,

I was talking to Marco Pivetta (Ocramius) about how to properly standardize service containers last weekend. So far, discussions circled around two approaches:

* Standardizing a service locator. In this case, multiple DI containers could be stacked, but optimizations of the composite container would be hard/impossible.

* Standardizing the service configuration. This is also difficult, since different DI containers choose (very) different ways of configuration.

We came up with the ID of standardizing service definition interfaces instead. Such a standard would define how to represent services, parameters and other definitions in PHP. Possible interface names are ServiceDefinitionInterface, ParameterDefinitionInterface etc.

In practice, such a standard would be used like this in a Composer environment:

* When the DI container is loaded, the service definitions of the different packages are loaded with appropriate loaders. For example, if a package contains a Symfony DI compliant services.yml file, a YamlLoader from the symfony/dependency-injection package is used. The result of each loader is a list of ServiceDefinitionInterface objects.

* The definition objects are merged and used as input for the DI container of the project. This container could keep the definitions in memory or compile an optimized container.

The benefits of such an approach:

* Packages may use different formats to define their services, as long as a loader exists that returns PSR-compliant definition objects.

* The project (root package) freely chooses its DI implementation which uses the PSR-compliant definition objects as source.

What do you think about this? I'd very much like to see someone working on this, however I don't have time to be the Editor here.

Cheers,
Bernhard

--

Sam Minnée

unread,
Jul 29, 2015, 6:02:34 PM7/29/15
to php...@googlegroups.com
Hi Bernard,

This sounds like it's addressing a similar challenge that I posted about in my previous email. I like what you're suggesting!

Did you have something like this in mind?

 - getServiceNames(): Get a list of all provided services
 - hasServiceDefinition($name): Check if a named service exists
 - getServiceDefinition($name): Return something to instantiate the services: perhaps it returns some kind of factory object, or perhaps just a callable?

One question I have: how much responsibility does the service definition factory have for setting properties, etc? Presumably it has to provide constructor arguments. What about setting explicitly mapped properties / calling configuration methods? What about auto-mapped properties based on service name / interface?

To put it another way: which of these actions should the DI container expect to do itself? Should the container be more of a dumb registry or should it have some injection intelligence of its own? Can we leave it up to individual implementations (so a Service Definition might set one property, and the DI container might set another) or will that lead to confusion?

Thanks,
Sam

--
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.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CAHjTTqPb3xyvr9Kz_X3OuA_%3DEpGxnrGkVyHA6-gUBRYC6odxvw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Bernhard Schussek

unread,
Jul 30, 2015, 4:43:09 AM7/30/15
to php...@googlegroups.com
Hi Sam,

Yes, I think we're more or less on the same page. The service definition factory (loader) returns an object model that *describes* the services. That is, getServiceDefinition($name) returns an object that describes *how* that service can be created. Some examples:

* class name + constructor arguments
* factory class + factory method
* service name + factory method
* configuration of setter calls
* ...

The DI container either directly uses that information or compiles it into an optimized PHP container class that is executed at runtime.

Callables would be difficult, since those can't be compiled. But callables can easily be replaced by static methods.

I don't have any better answer to your other questions but: probably :) Since the object model is described by interfaces, each container implementation could add additional information to their actual object model and use that information. However, we guarantee that the object models of all containers have an interoperable base set of features.

Cheers,
Bernhard

--

Matthieu Napoli

unread,
Jul 30, 2015, 5:25:47 AM7/30/15
to PHP Framework Interoperability Group, bsch...@gmail.com
Hi Bernhard,

This sounds like a path worth discussing, I find it interesting.

However I'm still skeptical on whether it could be successful, definitions (at least in PHP-DI) is a constantly moving piece. To give you an idea, here is the different types of definitions supported in PHP-DI:

- objects (or services)
- raw values, which some call parameters, but could be anything from a scalar to an object or a closure
- aliases (to another container entry)
- arrays (of services)
- factories, which is any callable, so it can be a closure
- string expressions, e.g. "{path.tmp}/cache" (allows to concatenate strings dynamically)
- decorator, which is a callable that takes the service and can return a decorated version (or can return anything else…), same as Pimple::extend() to give you an idea
- environment variables, it's like a parameter except it takes its value from an environment variable (and allows to set default values)

Those definitions are all so different that creating a single interface "that describes *how* that service can be created" isn't realistic I think. But maybe I misunderstood your suggestion?

However the approach in PHP-DI is each definition is handled by a "definition resolver", and can be debugged with a "definition dumper". I have a branch to try compiling the container and there it's the same pattern: each definition has its "definition compiler". With this pattern, it's possible to add a new definition by providing also its associated "resolver" and "dumper".

So interfaces are not really the core of the solution, but rather the combination of definition + its resolver + its dumper + its compiler. Maybe we could have a similar approach?

Example:


For each type (resolver, dumper, compiler), there's a "dispatcher" (dynamic dispatch design pattern) that calls the right implementation based on the type of the definition, example: https://github.com/PHP-DI/PHP-DI/blob/master/src/DI/Definition/Resolver/ResolverDispatcher.php#L18 So it's easy to add new supported definitions…

Tell me if I've completely missed the point :)

Matthieu

Bernhard Schussek

unread,
Jul 30, 2015, 7:41:40 AM7/30/15
to Matthieu Napoli, PHP Framework Interoperability Group
Hi Matthieu,

Thank you for your detailed input! You are right that there are many different ways to define services. While it's perfectly fine that DI implementations support all these variants, I think we can focus on standardizing those variants that we find most important.

In the end, something like this should be possible:

Maybe we need something like your resolvers etc. too. I'm not sure whether we do, but that's something we can figure out when we actually start working on the PSR.

Would you like to be Editor of this PSR?

Cheers,
Bernhard


--

Larry Garfield

unread,
Jul 30, 2015, 11:06:52 AM7/30/15
to php...@googlegroups.com
Hi Bernhard.

For background, my previously-written thoughts on PSR-11's
standardization of a service locator (ie, quite negative):

https://groups.google.com/forum/#!msg/php-fig/xC1CCjtyVnw/A3uZ-8HOaUIJ

And my counter-proposal for standardizing the service configuration (ie,
I like this idea):

https://groups.google.com/forum/#!msg/php-fig/xC1CCjtyVnw/EX2arfGmQ30J

(I figured it was easier to link than retype.)

In short, big +1 to a standard way to push configuration into a DIC, and
big +1 to only standardizing the common subset of tasks. (Ie, no need
to get into the complexity of compiler passes, many-layered factories,
etc.) But I believe it would be more effective as a parsable definition
file than a set of PHP interfaces. Either way, though, definitely more
useful and positive for the community than a SL standardization. I'd be
happy to see PSR-11 switch approaches to this instead. :-)
> --
> 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
> <mailto:php-fig+u...@googlegroups.com>.
> To post to this group, send email to php...@googlegroups.com
> <mailto:php...@googlegroups.com>.
> <https://groups.google.com/d/msgid/php-fig/CAHjTTqPb3xyvr9Kz_X3OuA_%3DEpGxnrGkVyHA6-gUBRYC6odxvw%40mail.gmail.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

--
--Larry Garfield

Paul M. Jones

unread,
Jul 30, 2015, 12:06:17 PM7/30/15
to php...@googlegroups.com

> On Jul 30, 2015, at 10:06, Larry Garfield <la...@garfieldtech.com> wrote:
>
> Hi Bernhard.
>
> For background, my previously-written thoughts on PSR-11's standardization of a service locator (ie, quite negative):
>
> https://groups.google.com/forum/#!msg/php-fig/xC1CCjtyVnw/A3uZ-8HOaUIJ

It's not "a service locator" so much as "a container", with functionality to set named services into the container, and to retrieve a service from the container for initial kickoff. It's only "a service locator" if it gets used like one.

Having a configuration specification *in addition to* PSR-11, and not *in place of PSR-11*, seems a laudable goal.


--
Paul M. Jones
pmjo...@gmail.com
http://paul-m-jones.com

Modernizing Legacy Applications in PHP
https://leanpub.com/mlaphp

Solving the N+1 Problem in PHP
https://leanpub.com/sn1php


Larry Garfield

unread,
Jul 30, 2015, 12:33:24 PM7/30/15
to php...@googlegroups.com
On 7/30/15 11:06 AM, Paul M. Jones wrote:
>> On Jul 30, 2015, at 10:06, Larry Garfield <la...@garfieldtech.com> wrote:
>>
>> Hi Bernhard.
>>
>> For background, my previously-written thoughts on PSR-11's standardization of a service locator (ie, quite negative):
>>
>> https://groups.google.com/forum/#!msg/php-fig/xC1CCjtyVnw/A3uZ-8HOaUIJ
> It's not "a service locator" so much as "a container", with functionality to set named services into the container, and to retrieve a service from the container for initial kickoff. It's only "a service locator" if it gets used like one.
>
> Having a configuration specification *in addition to* PSR-11, and not *in place of PSR-11*, seems a laudable goal.

OK Mr. Pedant. :-) As I noted in my original post (which IIRC you
agreed with), the problem with standardizing the "get from container"
process is it encourages Service Locator-style usage. "Standardizes a
service locator" is short hand for that. My feelings on that point
remain the same, but I am still in favor of a common way to put stuff
into the container.

--
--Larry Garfield

Paul M. Jones

unread,
Jul 30, 2015, 12:50:04 PM7/30/15
to php...@googlegroups.com
Hey Larry, just to you,


> On Jul 30, 2015, at 11:33, Larry Garfield <la...@garfieldtech.com> wrote:
>
> On 7/30/15 11:06 AM, Paul M. Jones wrote:
>>> On Jul 30, 2015, at 10:06, Larry Garfield <la...@garfieldtech.com> wrote:
>>>
>>> Hi Bernhard.
>>>
>>> For background, my previously-written thoughts on PSR-11's standardization of a service locator (ie, quite negative):
>>>
>>> https://groups.google.com/forum/#!msg/php-fig/xC1CCjtyVnw/A3uZ-8HOaUIJ

...

> As I noted in my original post (which IIRC you agreed with) the problem with standardizing the "get from container" process is it encourages Service Locator-style usage.

(/me grimaces)

Not to be abrasive (!) but I don't recall agreeing with that statement, and a quick scan of that thread does not reveal to me a message of my agreement. Perhaps another thread?

David Négrier

unread,
Jul 31, 2015, 5:51:24 AM7/31/15
to PHP Framework Interoperability Group, pmjo...@gmail.com
Hey Bernhard,

Just like Matthieu, I think your idea is worth exploring.

Here is my thought:

There are really 2 kinds of DI containers out there. The "runtime" containers and the "compiled" containers.

By standardizing "service definition interfaces", you make it possible for a "compiled" container to aggregate all instances in one place and therefore to achieve maximum performance. Therefore, you solve a problem that PSR-11 (in its current form) does not address.
However, you are also placing a performance hit on "runtime" containers. They will have to dynamically create each instance at runtime. This impact might be way bigger than the performance impact of the composite container not being optimised. Indeed, most runtime containers rely on closures to define services (because it's the most efficient way) and this is exactly the kind of thing we cannot easily put in a service definition.

So your proposal is best for compiled containers (Orno, Symfony, ZF...) but worst for the other containers out there (performance-wise).

Furthermore, from the point of view of "runtime" containers, what they only really need is the "ContainerInterface" (get/has)
Compiled containers on the other end need to be able to dump a definition as a PHP code string.

So rather than trying to describe a service definition (which is really difficult since there are plenty of types of definitions out there as Matthieu pointed out), maybe we should view the problem from the side of the consumer (i.e. the compiled container). What it needs is just to be able to dump a service definition into PHP code string.

So here is a proposal:

We keep PSR-11 get/has for runtime containers.
We discuss an interface for a ContainerBuilder that can "dump" PHP code directly:

interface ContainerBuilderInterface {
    /**
     * @return DumpableServiceInterface[]
     */
    function getServices();
}

interface DumpableServiceInterface {
    /**
     * @return string
     */
    function toPhpCode();
}


Eventually, this interface can be directly implemented in Symfony/ZF/Orno, and someone (maybe you Bernhard in Pulli) will come up with a compiler that compiles those services definition to a PSR-11 compatible container that can be added to any runtime container (using the delegate lookup feature and a CompositeContainer).

This way, we have both the best possible performance, and we are not shutting out the door to the containers that do not feature a compiler.

What do you think?

Bernhard Schussek

unread,
Jul 31, 2015, 6:55:21 AM7/31/15
to PHP Framework Interoperability Group, pmjo...@gmail.com
Hi David,

Seems like we're thinking along the same lines. You are right that this object model cannot be used efficiently by runtime containers. However, it *can* be used. So in any case, the problem of distributing packages that ship their own service definitions is solved.

If one wants to use package services in a project with a runtime compiler but without the performance drop, the definitions can be compiled with any suitable implementation. The compiled container, as long as it supports PSR-11, can then be hooked into the runtime container. Hence the performance problem is also solved and we end up with at most two container instances, which is fine by me. After all, you can also use a single compiled container if you care about maximum performance.

Combined with the PSR described here, I think that your PSR-11 proposal indeed has value.

Cheers,
Bernhard

--

--
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.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/478e7523-db45-4ae2-aaf2-4d6e6bdf9458%40googlegroups.com.

David Négrier

unread,
Jul 31, 2015, 8:42:56 AM7/31/15
to PHP Framework Interoperability Group, pmjo...@gmail.com, bsch...@gmail.com
Hi Bernhard,

Nice to see we are indeed thinking along the same lines!
I'm wondering whether we should consider including this "PSR-ContainerCompilerEntryDescriptor" (or whathever the name) directly into PSR-11 or if we should keep both as separated PSRs.

@mnapoli, @pmjones what do you think?

The whole concept is still in its infancy, while PSR-11 has been already discussed extensively. Do we want many small PSRs or one big "anything-container-related PSR"?

Also, I'll give the ContainerBuilderInterface idea a try, and maybe work on a prototype to see where this leads.

Cheers,
David.

Matthieu Napoli

unread,
Jul 31, 2015, 8:56:41 AM7/31/15
to David Négrier, php...@googlegroups.com, pmjo...@gmail.com, bsch...@gmail.com
Hi everyone,

While the idea is interesting, I think we shouldn't consider anything serious like "do another PSR", "look for editors", etc… while we don't have a proof of concept.

When we suggested the ContainerInterface idea few years ago, we got a strong opposition on the grounds that "it won't work" and "we should standardize concepts that work, not ideas". That was a valid criticism, and now container-interop is the proof that a ContainerInterface & co are actually useful, and used (by containers as well as frameworks).

I'm still skeptical of whether we can get somewhere with these ideas, but I'm interested to follow and contribute if I can. Should we work on a POC?

Matthieu
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/wQuVK2SXYkk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

David Négrier

unread,
Jul 31, 2015, 9:26:11 AM7/31/15
to PHP Framework Interoperability Group, david....@gmail.com, pmjo...@gmail.com, bsch...@gmail.com, matt...@mnapoli.fr
Hi Matthieu,

I'm definitely willing to work on a POC.
Shall we use the container-interop project for this? (after all, it is our test bed of anything container related :) )

Shall we start another project (i.e. container-interop/container-builder) or just increment the version number (i.e. put this in a future 1.2 branch?)

++
David.

David Négrier

unread,
Jul 31, 2015, 11:43:29 AM7/31/15
to PHP Framework Interoperability Group, david....@gmail.com
Ok, here we go for the POC.


I created 2 interfaces:


So far, the DefinitionInterface features only one function "toPhpCode". This is the most flexible implementation I could find. Using this strategy, the entry definition does render itself in PHP (rather than relying on an external PHP compiler). This is the most flexible approach as anyone can build a new DefinitionInterface and render pretty much anything in PHP. So we don't have to think all the possible use cases (constructor / setter injection / factories / lazy services, etc....). All this becomes an implementation detail!

I'll try in the coming weeks to write a simple Compiler (a class that takes an array of ContainerBuilderInterface and generates a compiled PHP file) and a set of classes implementing DefinitionInterface to implement the most common kind of entries (objects instantiated with new, aliases, etc...). Any help / comment / modification / implementation is welcome!

Larry Garfield

unread,
Jul 31, 2015, 4:58:59 PM7/31/15
to php...@googlegroups.com
Why are we trying to push this into a PHP interface? I already
demonstrated previously that a JSON string would easily be able to
handle the appropriate metadata, is simple to parse, and would work well
for runtime containers.

For reference, my off-the-cuff suggestion was:

disney/dwarf package, services.json:

{
'services': {
'dwarf_factory': {
'class': 'Disney\DwarfFactory'
}
'dwarf_manager': {
'class': 'Disney\DwarfManager',
'arguments': ['@dwarf_factory', 4]
}
}
}

And a runtime container like Pimple could read it in like so:

$services = json_decode(file_get_content('services.json'));
foreach ($services['services'] as $name => $def) {
$container[$name] = function($c) use ($def) {
if (!isset($def['arguments']) {
return new $def['class'];
}
$args = [];
foreach ($def['arguments'] as $argument) {
$args[] = strpos($argument, '@') !== false
? $c[substr($argument, 1)];
: argument;
}
return new $def['class'](...$args); // Yay, PHP 5.6!
}
}

Which is only marginally slower than manually doing all of the above,
and could still be compiled into PHP code to load for Pimple if one
really wanted to.

That strikes me as a much simpler and more flexible approach than
forcing a specific PHP interface. It emphasizes not the container but
the library that is providing services that have in-built dependencies
on each other, who would be the most important use case. (Just as in
PSR-3, the main target audience to benefit is the library authors who
want their code to work WITH a framework or application, not the
framework/application developers.)
> <https://github.com/container-interop/compiler-interop/blob/master/src/Interop/Container/Compiler/ContainerBuilderInterface.php>
> -https://github.com/container-interop/compiler-interop/blob/master/src/Interop/Container/Compiler/DefinitionInterface.php
>> Blog: http://webmozarts.com <http://webmozarts.com>
>> <https://groups.google.com/forum/#%21msg/php-fig/xC1CCjtyVnw/A3uZ-8HOaUIJ>
>>
>> ...
>>
>> > As I noted in my original post (which IIRC you
>> agreed with) the problem with standardizing the
>> "get from container" process is it encourages
>> Service Locator-style usage.
>>
>> (/me grimaces)
>>
>> Not to be abrasive (!) but I don't recall
>> agreeing with that statement, and a quick scan of
>> that thread does not reveal to me a message of my
>> agreement. Perhaps another thread?
>>
>>
>> --
>> Paul M. Jones
>> pmjo...@gmail.com
>> http://paul-m-jones.com <http://paul-m-jones.com>
>>
>> Modernizing Legacy Applications in PHP
>> https://leanpub.com/mlaphp
>> <https://leanpub.com/mlaphp>
>>
>> Solving the N+1 Problem in PHP
>> https://leanpub.com/sn1php
>> <https://leanpub.com/sn1php>
>>
>>
>> --
>> 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.
>> To post to this group, send email to
>> php...@googlegroups.com.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/php-fig/478e7523-db45-4ae2-aaf2-4d6e6bdf9458%40googlegroups.com
>> <https://groups.google.com/d/msgid/php-fig/478e7523-db45-4ae2-aaf2-4d6e6bdf9458%40googlegroups.com?utm_medium=email&utm_source=footer>.
>>
>>
>> For more options, visit
>> https://groups.google.com/d/optout
>> <https://groups.google.com/d/optout>.
>>
>>
>> --
>> 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/wQuVK2SXYkk/unsubscribe
>> <https://groups.google.com/d/topic/php-fig/wQuVK2SXYkk/unsubscribe>.
>> To unsubscribe from this group and all its topics, send an
>> email to php-fig+u...@googlegroups.com.
>> To post to this group, send email to php...@googlegroups.com.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/php-fig/a3306bc9-6c4e-4a3b-909e-712163ffc02e%40googlegroups.com
>> <https://groups.google.com/d/msgid/php-fig/a3306bc9-6c4e-4a3b-909e-712163ffc02e%40googlegroups.com?utm_medium=email&utm_source=footer>.
>> For more options, visit https://groups.google.com/d/optout
>> <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
> <mailto:php-fig+u...@googlegroups.com>.
> To post to this group, send email to php...@googlegroups.com
> <mailto:php...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/bbafbc26-f4a9-4ddc-8a80-de9ed4a0b18e%40googlegroups.com
> <https://groups.google.com/d/msgid/php-fig/bbafbc26-f4a9-4ddc-8a80-de9ed4a0b18e%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

--
--Larry Garfield

Bernhard Schussek

unread,
Jul 31, 2015, 7:46:00 PM7/31/15
to PHP Framework Interoperability Group
I don't think that a toPhpCode() method is the way to go. This way, compilers have no possibility of inspecting and optimizing the service definitions.

Also, I don't think that we need to standardize ContainerBuilderInterface, but I could be mistaken. After all, we only want to guarantee that different containers can use the same definitions as input. How the containers themselves are structured shouldn't be relevant to us.

@Larry: I understand your approach with JSON, but I think that this will make the creation of a PSR harder than necessary. PHP gives us the ability to define very precise interfaces - with JSON, that's much more difficult. Also, this means that we need one more additional conversion step for containers that don't use the same JSON format as configuration (own format -> JSON -> object model -> compiled code). My last counter-argument to JSON is that it's not as easily extensible as a PHP interface. IMO interfaces are the way to go here.

--
Bernhard Schussek
Blog: http://webmozarts.com

David Négrier

unread,
Aug 3, 2015, 5:34:55 AM8/3/15
to PHP Framework Interoperability Group
Hey Larry,

Sorry, I previously did not take time to answer your proposal about a JSON string to define services, but I see two major problems that make your proposal quite unlikely to reach a consensus: the format and the features.

Regarding the format:

You chose JSON. I'm confident enough that most people will not find it a good idea. If you look at the problem from the point-of-view of containers that have a compiler (SF, Nette, ZF), JSON has many drawbacks:

- it does not feature comments (unlike XML or YML)
- it does not have a file descriptor (unlike XSD for XML)
- it can be more verbose than other alternatives (YML for instance)

Some framework have come to great length to invent a new file format that is specially tailored for DI (I think Nette has)... so just saying "let's go with JSON" will certainly be heavily challenged... but wait, I've only been talking about containers that have a compiler.

If you look at your proposal from the perspective of containers that do not have a compiler, the sole fact of reading a configuration file is a complete show-stopper. 

You said using a JSON file is "marginally" slower and I beg to disagree. Just imagine an application with 50 packages, each coming with its own JSON configuration file. For runtime containers, it means 50 calls to "file_get_content" and 50 calls to "json_decode" on EACH HTTP request. This will simply be way too slow.

From the perspective of runtime containers, there are only 2 options: using PHP arrays to store the configuration, or compiling JSON files into a PHP container.

Solution 1 does work. Rather than using a JSON file, you put your configuration in a PHP file that returns an array.

For instance, using your sample, we could convert your JSON into this PHP file:

<?php
return [
'services' => [ 
    'dwarf_factory' => [ 
        'class' => 'Disney\DwarfFactory' 
    ] 
    'dwarf_manager' => [ 
      'class' => 'Disney\DwarfManager', 
      'arguments' => ['@dwarf_factory', 4] 
    ] 
  ] 
];

PHP files are cached by the OPCache and therefore are loaded instantly. Mouf has been using this very technique for the past 6 years and I can tell you performance is ok. The problem, of course, is that the frameworks that have a compiler will be less than thrilled to see a PHP configuration file. It is harder to analyze than YML or JSON as you cannot see what it contains without actually executing the PHP in it (no static analysis)... so it features a number of issues.

The other solution is to compile JSON files into a PHP container... but remember, I'm specifically speaking about containers that have no compiler (which by the way, are a majority out there). Do you think Pimple would implement a compiler? (it would simply explode its 80-lines of code). Hopefully, there is another way around this. Someone could write an external library that would compile all configurations files into a PHP container. Then every framework could hook that container to their own container... which is exactly what PSR-11 is about (this is the delegate dependency lookup pattern!) So even if you managed to have a unique JSON format agreed upon, you would still need PSR-11's ContainerInterface as a prerequisite.

Regarding the features:

Your JSON file is very minimalistic in the features it provides. I understand this is "by design", but this design is very opiniated. You assume that the only way on object should be created is by calling its constructor and passing parameters. But there are plenty of other ways to create an object.

- You might want to call a factory method (e.g. Factory::getObject)
- You might want to create an object and then perform some method calls (for instance, you might want to call "setters"). Setters are important because in the case of a circular dependency between services (it happens at times...), they are the only way to go.
- You might want to create an object using a callback (Pimple style, which is really the most flexible strategy)

Also, a number of interesting features are out of scope of your JSON file:

- Autowiring: what is the use of having a configuration file if you can simply discover the dependencies based on type hinting?
- Lazy services: what if I want to wrap a service into a proxy in order to boost performance if my service is never called?
- Aliases: might be useful to close a gap between 2 frameworks with different naming conventions

With your JSON file, you are forcing anyone to use the lowest common denominator of DI. Using an interface, we can have instead the greatest common divisor.
Using one of the interfaces Bernhard or I are proposing, it is fairly easy to implement your JSON format. But rather than forcing anyone into the same format (which is a bit of a dictatorship approach), the competition stays open between frameworks. Your JSON format becomes one possible format amongst many possible implementations. We don't close the game, we open it to competition and innovation. So I definitely believe the interface is the way to go here.

++
David.

David Négrier

unread,
Aug 3, 2015, 5:50:50 AM8/3/15
to PHP Framework Interoperability Group
@Bernhard,

I understand your point.

In your scenario, we feed the compiler with a "service definition" that goes in great length describing how the service is to be created, and it is up to the compiler to generate PHP code and to optimize it. Different compilers can have different strategies to compile PHP code.

In my scenario, the compiler is completely dumb. "Services definition" objects know how to generate PHP code. I understand that it feels a bit weird, but this is also the most flexible approach as anyone can come up with a new "service definition" object.

I really think we should toy a bit with prototypes in order to see how this can work.

To be honest, I'm not 100% sure my idea is the best approach. The idea is quite new to me, but my gut feeling is that by pushing the PHP code generation out of the compiler, we are making something that is really more flexible.

I have not a lot of time right now and I'd like to build a prototype based on the "toPhpCode" approach and see where this leads. However, I would be very interested into seeing what you can come up with your idea. Do you think you can find some time into building a small prototype or would anyone here be willing to?

++
David.

Larry Garfield

unread,
Aug 3, 2015, 3:20:52 PM8/3/15
to php...@googlegroups.com
On 8/3/15 4:34 AM, David Négrier wrote:
> Hey Larry,
>
> Sorry, I previously did not take time to answer your proposal about a
> JSON string to define services, but I see two major problems that make
> your proposal quite unlikely to reach a consensus: the /format/ and
> the /features/.
>
> Regarding the format:
>
> You chose JSON. I'm confident enough that most people will not find it
> a good idea. If you look at the problem *from the point-of-view of
> containers that have a compiler* (SF, Nette, ZF), JSON has many drawbacks:
>
> - it does not feature comments (unlike XML or YML)
> - it does not have a file descriptor (unlike XSD for XML)
> - it can be more verbose than other alternatives (YML for instance)

I chose JSON as an example because it's simple, universally supported in
PHP, and very fast to parse (because it's built-in to PHP). I am not
wedded to it, however. My point was simply that we should focus on a
declaration file, rather than executable code with interfaces. Believe
me, I am *very* acutely aware of the bikeshed potential of picking a
config file format. :-)

> Some framework have come to great length to invent a new file format
> that is specially tailored for DI (I think Nette has)... so just
> saying "let's go with JSON" will certainly be heavily challenged...
> but wait, I've only been talking about containers that have a compiler.
>
> If you look at your proposal *from the perspective of containers that
> do not have a compiler*, the sole fact of reading a configuration file
> is a complete show-stopper.
>
> You said using a JSON file is "marginally" slower and I beg to
> disagree. Just imagine an application with 50 packages, each coming
> with its own JSON configuration file. For runtime containers, it means
> 50 calls to "file_get_content" and 50 calls to "json_decode" on EACH
> HTTP request. This will simply be way too slow.

Runtime containers have a setup cost for every service, period. If data
is pulled from somewhere, it needs to get mapped into the container.
That mapping process has cost. Without building them all and
benchmarking it I cannot say with certainty what the relative costs are,
but my gut feeling is that mapping everything through interface calls is
going to be slower than you think, ie, not as much faster than parsing a
file (especially if you're on a modern kernel that properly caches disk
IO). Without comparable implementations though we're both guessing.

> From the perspective of runtime containers, there are only 2 options:
> using PHP arrays to store the configuration, or compiling JSON files
> into a PHP container.
>
> Solution 1 does work. Rather than using a JSON file, you put your
> configuration in a PHP file that returns an array.
>
> For instance, using your sample, we could convert your JSON into this
> PHP file:
>
> <?php
> return [
> 'services' => [
> 'dwarf_factory' => [
> 'class' => 'Disney\DwarfFactory'
> ]
> 'dwarf_manager' => [
> 'class' => 'Disney\DwarfManager',
> 'arguments' => ['@dwarf_factory', 4]
> ]
> ]
> ];
>
> PHP files are cached by the OPCache and therefore are loaded
> instantly. Mouf has been using this very technique for the past 6
> years and I can tell you performance is ok. The problem, of course, is
> that the frameworks that have a compiler will be less than thrilled to
> see a PHP configuration file. It is harder to analyze than YML or JSON
> as you cannot see what it contains without actually executing the PHP
> in it (no static analysis)... so it features a number of issues.

Is that really harder, though? You can tokenize the file if you want to
avoid loading it into memory:

http://php.net/manual/en/function.token-get-all.php

But if you're going to be loading it into your container anyway, you'll
want to load/parse it and then manipulate the resulting array.

> The other solution is to compile JSON files into a PHP container...
> but remember, I'm specifically speaking about containers that have no
> compiler (which by the way, are a majority out there). Do you think
> Pimple would implement a compiler? (it would simply explode its
> 80-lines of code). Hopefully, there is another way around this.
> Someone could write an external library that would compile all
> configurations files into a PHP container. Then every framework could
> hook that container to their own container... which is exactly what
> PSR-11 is about (this is the delegate dependency lookup pattern!) So
> even if you managed to have a unique JSON format agreed upon, you
> would still need PSR-11's ContainerInterface as a prerequisite.

I do not follow this logic.

Uncompiled containers have an upper bound of usefulness, because
otherwise the time spent on each request configuring the container with
services goes up with each service registered. That's why compiled
containers exist, to eliminate that overhead. They add a fair bit of
complexity for that, sure; that's the trade-off between the two (yes,
entirely legitimate) ways of configuring a container.


> Regarding the features:
>
> Your JSON file is very minimalistic in the features it provides. I
> understand this is "by design", but this design is very opiniated. You
> assume that the only way on object should be created is by calling its
> constructor and passing parameters. But there are plenty of other ways
> to create an object.

*snip*

It was an off-the-cuff minimalist example to demonstrate the feasibility
of a config-file approach. It was not intended as a be-all-end-all
final implementation and I never represented it as such. Adding a
factory key or whatever is totally fine.

Although remember, as I understand it we're talking about a tool to make
it easy for packages to expose themselves to frameworks and
applications. In practice, I suspect the vast majority won't need most
complex features and functionality. If a specific framework needs it
for some reason (lazy loading is a very good example), well, fine, do
whatever your framework does for lazy loading via an alter, compiler
pass, extra 2 lines of setup code, or what have you. We don't need to
solve the 99% case, only the 80% case.

> With your JSON file, you are forcing anyone to use the *lowest common
> denominator* of DI. Using an interface, we can have instead the
> greatest common divisor.
> Using one of the interfaces Bernhard or I are proposing, it is fairly
> easy to implement your JSON format. But rather than forcing anyone
> into the same format (which is a bit of a dictatorship approach),

Dictatorship? That's hyperbolic. Any standard, by definition, implies
"yes, you DO have to do it this way to get these benefits". By that
logic, everything FIG does is dictatorial, which is nonsense.

> the competition stays open between frameworks. Your JSON format
> becomes one possible format amongst many possible implementations. We
> don't close the game, we open it to competition and innovation. So I
> definitely believe the interface is the way to go here.

I'm not sure we're even having the same discussion at this point... The
goal would be to provide one(1) way for libraries to expose their
services to a framework, to eliminate the bridge-module-per-library
problem. That way, only one adapter is needed per container
(cardinality c) rather than one per container and library (cardinality
c* l). In that case, "lots of competition" is counter-productive;
that's the problem we have today, that we're trying to solve!

--
--Larry Garfield

David Négrier

unread,
Aug 4, 2015, 12:57:45 PM8/4/15
to PHP Framework Interoperability Group
Le lundi 3 août 2015 21:20:52 UTC+2, Larry Garfield a écrit :

snip.

Runtime containers have a setup cost for every service, period.  If data
is pulled from somewhere, it needs to get mapped into the container.  
That mapping process has cost.  Without building them all and
benchmarking it I cannot say with certainty what the relative costs are,
but my gut feeling is that mapping everything through interface calls is
going to be slower than you think, ie, not as much faster than parsing a
file (especially if you're on a modern kernel that properly caches disk
IO).  Without comparable implementations though we're both guessing.


 snip.


Uncompiled containers have an upper bound of usefulness, because
otherwise the time spent on each request configuring the container with
services goes up with each service registered.  That's why compiled
containers exist, to eliminate that overhead.  They add a fair bit of
complexity for that, sure; that's the trade-off between the two (yes,
entirely legitimate) ways of configuring a container.


I think that your view of what a container should be is very Symfony-centric.
Runtime containers don't necessarily have a setup cost that increments with the number of services.

Yes, Pimple has this cost, because you must assign each instance individually, but there are other ways around this.
Take a look at Picotainer. This is a very minimalistic container (21 lines of code, smaller than Pimple!) You initialize it using an array of callables.
I can have as many services as I want declared here with absolutely no performance penalty. And actually, I'm having performances that are on-par with a compiled container here.
In Mouf, I'm having a huge PHP configuration array that is loaded instantly because of Opcode cache. I have projects with thousands of instance that are as fast as a small project with 10 instances.
If you have a look at containers based on autowiring, there is no limit either, since services are not declared but discovered.

What I mean is that a runtime container is not necessarily a sub-container compared to containers that feature compilers. Runtime compiler developers are using a huge number of tricks to have great performances, but they have heavy constraints on what they can and what they cannot do. Loading a configuration file in any format except PHP array would rather trigger the red flag (or require preprocessing / caching of these files)

 

> Regarding the features:
>
> Your JSON file is very minimalistic in the features it provides. I
> understand this is "by design", but this design is very opiniated. You
> assume that the only way on object should be created is by calling its
> constructor and passing parameters. But there are plenty of other ways
> to create an object.

*snip*

It was an off-the-cuff minimalist example to demonstrate the feasibility
of a config-file approach.  It was not intended as a be-all-end-all
final implementation and I never represented it as such.  Adding a
factory key or whatever is totally fine.

Ok.
 

Although remember, as I understand it we're talking about a tool to make
it easy for packages to expose themselves to frameworks and
applications.  In practice, I suspect the vast majority won't need most
complex features and functionality.  If a specific framework needs it
for some reason (lazy loading is a very good example), well, fine, do
whatever your framework does for lazy loading via an alter, compiler
pass, extra 2 lines of setup code, or what have you.  We don't need to
solve the 99% case, only the 80% case.

Whether we want 80% or 99% is up to the FIG members to decide. If I look at PSR-6, I'd rather think the members of the FIG are headed towards covering the 99% otherwise, the get/has/set cache interface would have made more sense!
 

> With your JSON file, you are forcing anyone to use the *lowest common
> denominator* of DI. Using an interface, we can have instead the
> greatest common divisor.
> Using one of the interfaces Bernhard or I are proposing, it is fairly
> easy to implement your JSON format. But rather than forcing anyone
> into the same format (which is a bit of a dictatorship approach),

Dictatorship?  That's hyperbolic.  Any standard, by definition, implies
"yes, you DO have to do it this way to get these benefits". By that
logic, everything FIG does is dictatorial, which is nonsense.

> the competition stays open between frameworks. Your JSON format
> becomes one possible format amongst many possible implementations. We
> don't close the game, we open it to competition and innovation. So I
> definitely believe the interface is the way to go here.

I'm not sure we're even having the same discussion at this point... The
goal would be to provide one(1) way for libraries to expose their
services to a framework, to eliminate the bridge-module-per-library
problem.  That way, only one adapter is needed per container
(cardinality c) rather than one per container and library (cardinality
c* l).  In that case, "lots of competition" is counter-productive;
that's the problem we have today, that we're trying to solve!

Ok, I'm pretty sure we are not having the same discussion here. Let me try to rephrase.

The original idea expressed by Bernhard is to have the possibility for libraries to expose their services in a standard compliant way to a framework.
Typically, each library would choose a "parser library" (I don't like this name but I could not find a better one)
The role of this parser library is:
- to define a configuration file format (JSON / YML / whatever format we can imagine, this is up to the parser library)
- to read this configuration file from loaded packages
- to transform this configuration file into a set of container entry definitions (a definition is a class that describes an entry in a container). The definition is standardized (there is a DefinitionInterface approved by FIG)

Then, any compliant-compiler can:
- read these definitions
- compile them into the compiled container

As a bonus, for the guys who are using a runtime container, it would be possible to add a minimalistic compiled container next to the runtime container and hook to it via PSR-11.

So yes, the goal is to eliminate the bridge-module-per-library problem, and the cardinality is c + l (and not c * l )  (one implementation per (compiled) container, and one by file format (the parser-library).

The idea here is to avoid having a difficult debate about the file format. It is unlikely that FIG can come up with a consensus on this. Instead, anyone willing to propose its own file format could do it, and hopefully, we would see maybe 2 or 3 "parser library" used, or maybe 1 format would begin to be wildly used and become a de-facto standard (just like Composer is a de-facto standard for packaging but has been made possible thanks to PSR-0).

I'm working on a prototype of this to see if this can actually work. In my opinion, the DefinitionInterface can offer an advantage over defining a file format if we can keep the DefinitionInterface relatively small (compared to the full description of a file format).

--
David Négrier

David Négrier

unread,
Aug 12, 2015, 11:10:38 AM8/12/15
to PHP Framework Interoperability Group
Hey Bernhard, hey list,

@Bernhard, about one week ago, you told me:

> Also, I don't think that we need to standardize ContainerBuilderInterface, but I could be mistaken. After all, we only want to guarantee that different containers can use the same definitions as input. How the containers themselves are structured shouldn't be relevant to us.

I've been thinking quite a lot about this comment, and you might certainly be right. If this PSR comes out, we might want to leave out of scope the part explaining how we "discover" the definitions.

Still, even if this is out of the PSR's scope, we will need to think of a technique to discover the definitions automatically (or semi automatically)

When I wrote the ContainerBuilderInterface, I was thinking about doing something similar to the "Bundle" system in Symfony (i.e. a class that provides the definitions to the compiler). Then, I read more about Puli and I realized that there was other ways to do this. In particular "discovery" is part of the things that Puli is doing well :)

I'm not completely sure how things should work but here is an idea I've had, and I'd like some feedback about it. So here we go:

- Step 1:  Using Puli, one can *provide resources*. In our case, a JSON file (or YML file, or PHP file or whatever) containing entry definitions (let's call this hypothetic format "JSON-DI") could be a resource. So packages providing JSON-DI files could declare they are providing those resources using Puli (http://docs.puli.io/en/latest/discovery/providing-resources.html)
- Step 2: Now, we need a package that knows how to parse those JSON-DI files, and transform those into "definition objects". This package would *consume* JSON-DI resources (http://docs.puli.io/en/latest/discovery/consuming-resources.html). It would also *provide* "definition objects".
- Step 3: Finally, a framework (with a compiled container) would *consume* definition objects. Alternatively, for frameworks without a compiler, a third-party compiler package can *consume* definition objects and provided a PSR-11 compliant compiled container that could in turn be *consumed* by a framework with a runtime container.

Step 1 seems relatively easy.
I'm getting a bit lost at step 2 and step 3. I understand that Puli is about "resources" (i.e. **files**). The JSON-DI files qualify as resources.
Now, if I want to publish and consume "definition objects", I have a few questions. If I want to stick to Puli's philosophy, I could create a ".di.php" file format that would "return" an array of definitions. The code would look like this:

<?php
// The parseDefiinitions class finds the files (using the $discovery)
// Then, it parses the files, and returns definition objects.
// Note: it is not clear how the $discovery object is instanciated.
return JsonDiFileParser::parseDefinitions($discovery);

But then, I'm not sure how the $discovery object should be injected in the file.

As an alternative, we could imagine that the package containing the JSON-DI parser does not *provide* files, but rather, it provides a *class* (implementing an interface similar to ContainerBuilderInterface).

The class would look like this:

class JsonDiFileParser implements ContainerBuilderInterface {
    // By convention, the first parameter of the constructor MUST be the discovery object.
    public function __construct(Discovery $discovery) { ... }

    public function getDefinitions(....);
}

It feels a bit weird to have to force the first parameter of the constructor to be a discovery, but we don't have any DI container (since we are in the process of building it), so I guess we have to rely on conventions.

Also, in this idea, I'm providing/consuming classes (i.e. not files). I'm not sure if Puli can do that right now. I saw that you can attach metadata to resources. In this particular case, we would have a resource that is not a file, only set of metadata. Is it possible in Puli? If not, does the idea sound foolish or sensible?

So here I am. These are the 2 options I'm seeing right now. I'm not sure which one is best, or if you guys would have another option that seems more sensible. @Bernhard, I guess you have been thinking quite a lot about this provider/consumers thing. Any chance you might have an advice or a better idea than I've had?

++
David.





--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.

To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

David Négrier

unread,
Sep 4, 2015, 5:59:16 AM9/4/15
to PHP Framework Interoperability Group
Here is a quick follow-up to the quest for a DefinitionInterface (an interface describing entry definitions)

A few months ago, in this thread, I told I would be investigating this... and I did. I wrote a lengthy blog article about it here describing what steps I went through:

http://www.thecodingmachine.com/definitioninterface-toying-php-di-compiled-containers-interoperability/

(it's really a long piece of text, not for the faint hearted so I'm not pasting it on this mailing list).

In short:

I tested the possibility to write a DefinitionInterface with a "dumb" compiler (code generation happens in the definition objects rather than in the compiler). This has a number of advantages but also drawbacks (described in the blog article)

In the process, I wrote a proof of concept (actually, I even iterated over this POC once to improve performance):

- moufmouf/compiler-interop => the package containing the DefinitionInterface
- mouf/common-container-definitions => the package containing classes implementing of the DefinitionInterface
- thecodingmachine/yaco => the compiler

My conclusions? Designing a DefinitionInterface is really hard. The "dumb compiler" approach is pretty much ok regarding optimization and performances. It lacks the "reflection" of a more descriptive interface, but a more descriptive interface is almost impossible to design (see my blog article for details). In the end, I really could not find yet a solution that I find appealing.

I'd be very interested in gathering feedback here, or more ideas. If someone wants to give another try at this interface (maybe with a different approach), I'd be very interested in seeing where this leads. In the meantime, I'm starting to wonder if trying to design an interface for this is a good idea. There are alternatives, like writing an interop package that provides full implementation for entry definitions and that can be plugged into all existing containers (through compiler passes)

Anyway, any comment is welcome here!

++
David Négrier
http://mouf-php.com
Github: @moufmouf
Twitter: @david_negrier
To unsubscribe from this group and all its topics, send an email to php-fig+unsubscribe@googlegroups.com.

Bernhard Schussek

unread,
Dec 15, 2015, 6:40:44 AM12/15/15
to php...@googlegroups.com
Another follow-up from me. After thinking about this some more, I agree with Larry that standardizing a configuration format is really the best and easiest way to solve this issue.

* A standardized configuration can be turned into whatever format you need. Runtime containers can simply dump a PHP file in a "composer update" hook that contains a plain array of closures. These closures can simply be merged with the user-defined ones.

* Standardized configuration can be parsed by external tools. For example, IDE's may choose to support parsing of PSR-11 configuration files. Packagist could display dependency graphs for Composer packages. Many things are possible.

* Standardized interfaces, on the other hand, require a lot of additional infrastructure and complexity. We need to know which package contains which configuration format, which loader to use and how to construct the loader. Also, we potentially need to use several different loaders from different DIC vendors at the same time, which means wall those DICs need to be downloaded by Composer.

I think we should drop this topic and start a new one to find (i) a common configuration format and (ii) the scope that we want to cover in the PSR.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/6ceb7b5b-98d1-4575-947c-28d4cfddadc7%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.

To post to this group, send email to php...@googlegroups.com.

Paul Dragoonis

unread,
Dec 15, 2015, 7:30:54 AM12/15/15
to php...@googlegroups.com
I fully support the direction of configuration format standarisation.

With PPI, being an interop framework, we're very focused on container-interop and this topic and I'm very excited to see what we can achieve with it. 

Meanwhile, until there is a standard - in PPI we will continue to create our own working standard to use in the wild for production applications and be able to give real-world input on how things are working in practise, rather than just theory.

Feel free to drag me into conversations if I don't spot them, I'll do my best to keep a close eye on this topic.


David Négrier

unread,
Dec 15, 2015, 9:22:37 AM12/15/15
to PHP Framework Interoperability Group
Hey all,

We had a meeting 3 weeks ago at Paris ForumPHP regarding PSR-11 and anything container related. Matthieu Napoli and myself met Bernhard and Beau there, and after quite some discussion, we started working on... interfaces regarding definition interoperability.

Actually, we spent most of our free time last month working on container definition interfaces (with Matthieu Napoli, Maxime Fabre and many others...)

We wanted to test the idea more thoroughly before speaking about it on the FIG mailing list, but seeing the last message here, it's maybe time we present the project.

You can have a look at a proposal for definition interfaces here: https://github.com/container-interop/definition-interop/

We have made quite a lot of progress, and I think this approach deserves to be considered seriously before we start all over again in (yet) another direction.

We already have a lot of projects working with these interfaces:

- 1 container that accepts definitions compatible with definition-interop
- 1 compiler that accepts definitions compatible with definition-interop and generates a PSR-11 container from these
- a Symfony bundle (on the way)
- a YML parser that parses Symfony-like YML services file and generates definitions
- a few sample packages that generate services
- ...

Also, I just finished (1 hour ago) a proof-of-concept using definition-interop and Puli that automatically detects service definitions (declared in a YML file), parses them into PHP definition objects (implementing definition-interop) and then compiles them into a container (a PSR-11 compatible container :) ).
I plan to blog about it, but that might take me one day or two.

@Bernhard, I fail to see how a standardized configuration file is any better than a common definition interface in most of the points you state:


* A standardized configuration can be turned into whatever format you need. Runtime containers can simply dump a PHP file in a "composer update" hook that contains a plain array of closures. These closures can simply be merged with the user-defined ones.

True, but this is also true from a definition interfaces. They can be dumped into a container (see Yaco that does exactly that)

* Standardized configuration can be parsed by external tools. For example, IDE's may choose to support parsing of PSR-11 configuration files. Packagist could display dependency graphs for Composer packages. Many things are possible.

This is somehow true. But also completely possible with a standardized definition provider interface. Your IDE support point is however true.


* Standardized interfaces, on the other hand, require a lot of additional infrastructure and complexity. We need to know which package contains which configuration format, which loader to use and how to construct the loader. Also, we potentially need to use several different loaders from different DIC vendors at the same time, which means wall those DICs need to be downloaded by Composer.

I completely disagree here. You do indeed need to fetch loaders from composer, but that does not mean you need to fetch DICs. The whole point of definition-interop is to NOT fetch DICs (compared to PSR-11). Regarding infrastructure and complexity, I'll write a blog post explaining my proof-of-concept. You can then decide if it is too complex or not.

Also, in my opinion, a standardized configuration file will be very very difficult to agree upon. See how things are dragging along on this mailing-list. It took 4 years (!) for PSR-6 to pass, and cache is really an easier problem than dependency injection. You will have to agree on the format (JSON? XML? YAML? NEON? PHP?), you will have to agree on the scope... And at the end of the day, since the format is a standard, it won't be possible to enhance it.
Plus you need people motivated on working on the subject. Since this thread started (6 months ago!), the only ones who have actively been working on the topic are the same people that worked on container-interop... (that's Matthieu and myself) I mean, I see a lot of criticism on the FIG mailing list, but very little support or active contributions. A definition interface is a lower hanging fruit (even if it is hanging quite high already...)

That being said, since we are going public, I'd be very happy to get as many comments as possible on what was already achieved on definition-interop here: https://github.com/container-interop/definition-interop/ And at the end of the day, even if you prefer a standardized file format, the issues with the scope of definitions are common to the 2 approaches so that's worth having a look at.

David.

Matthieu Napoli

unread,
Dec 15, 2015, 11:11:14 AM12/15/15
to php...@googlegroups.com
Hi everyone!
 
As David said we are introducing the project a bit earlier that we planned, so it's not as stable and documented as we'd like it to be. But we are also at the point where it would be very interesting to get feedback from contributors/maintainers of frameworks based on modular architectures.
 
Basically: we want to assess whether the solution we are playing with could cover the minimum necessary to write framework agnostic modules (aka bundles/plugins…). Results from our experiments so far are quite positive.
 
By the way we are both available saturday morning at 11am CET (Paris/Central Europe time), which is also 10am for UK/GMT or for people living in different countries: http://www.worldtimebuddy.com/?qm=1&lid=2992090,2988507,2643743&h=2992090&date=2015-12-19&sln=11-12 We are planning to meet on Hangout, if you want us to explain what we started feel free to reach out to us and join. If you are interested but can't join I'm sure we can find another time to explain it to you, feel free to email us privately. You are also welcome to join the Gitter chat room: https://gitter.im/container-interop/definition-interop (the room is public and only requires a GitHub account to login)
 
Matthieu

David Négrier

unread,
Dec 15, 2015, 1:10:16 PM12/15/15
to PHP Framework Interoperability Group
And here is a link to the blog article I just wrote to explain our concept of definition interfaces. The article is actually going a bit further than the initial scope of definition-interop as I'm exploring how we could use Puli's discovery features to automatically detect definition files.

http://www.thecodingmachine.com/interoperable-php-packages-with-definition-interop-and-puli/

There is a link at the end of the article to a proof-of-concept. It is a (barely working) application that automatically detects a Doctrine cache service, compiles the service into a container and instantiates the service. Completely work in progress, but promising enough.

Best regards,
David.

Bernhard Schussek

unread,
Dec 15, 2015, 1:24:11 PM12/15/15
to php...@googlegroups.com
Hi David, hi Matthieu,

Thank you for the work you are putting into this! Your work in figuring out how to make this work is very valuable.

It took me until today to realize that a standardized service configuration and standardized service interfaces are essentially the same thing: representations of service definitions, however in different formats.

The main difference between the two is the number of moving parts required to implement them:

* For standardized configuration you need one container implementation, one PSR-11-compliant loader for that container. All in all, that's one Composer package (the DIC).

* For standardized definitions, you need more moving parts: First, you need one container implementation. But then you need one loader for each configuration format used by your Composer dependencies (Symfony, PHP-Di, Zend, etc.). These loaders are typically provided by the corresponding DIC package (e.g. the Symfony loader comes with the Symfony DIC component), which means those packages must be installed first. Also, you need to do duplicate discovery: discover all configuration files, and then discover the loaders for those files. That's exactly what we were talking about in Paris.

While both cases are doable, the second involves quite a bit of smartness. I prefer simple solutions as long as they are just as powerful. Simple means easy to understand, and easy to agree upon. And - after thinking everything through one more time - I think that we can achieve the same power with standardized configuration.

I'll try to join your hangout on Saturday.

Cheers,
Bernhard


Matthieu Napoli

unread,
Dec 16, 2015, 2:44:13 AM12/16/15
to php...@googlegroups.com
Hi Bernhard,

Below are arguments against a standard configuration format:

- validation when writing: except XML there is no validation for JSON, YAML, etc
- choosing the format: it will be one hell of a discussion… everyone has a reason to hate one or the other (XML because it's clunky to write, JSON because it's verbose and has no comments, YAML because it's so prone to errors, etc.)
- discovery: resources need to be discovered

The last one is the biggest issue IMO. We could introduce resource location or even Puli in the equation but it would make the whole standard much bigger (even though I like Puli a lot). As an example PSR-3 didn't include PSR-0, or even Composer… So I think configuration files should be decoupled from resource location. And until that problem is solved, configuration files will be unusable.

On the other hand discovery is solved for PHP classes thanks to PSR-4. The way we solved it at the moment for definition-interop is to have a Definition Provider (https://github.com/container-interop/definition-interop/blob/master/src/DefinitionProviderInterface.php). To register a "module" (aka bundle, etc.) you simply do:

$app->register(new TwigProvider);
$app->register(new DoctrineProvider);
// both providers implement DefinitionProviderInterface

Yes that means instantiating and registering modules manually, but that's exactly how it works with Symfony bundles, Silex service providers, Slim service providers, etc. So it sounds like a good solution.

Anyway, I just want to say I'm not 100% against standard configuration files and I know definition interfaces have issues too, I'm just pushing the discussion further.

Matthieu
Reply all
Reply to author
Forward
0 new messages