Each framework has it's own custom package format. 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 package format that could be supported by all frameworks.
In the rest of this post, I'll highlight a list of "abilities" that this package format should have.
The very first thing our package format should be able to do is to put things in a container.
If I wrote a new "logger" package, with a MyLogger class in it, I want to register an entry in the
container containing a MyLogger instance.
In other words, the PSR should be able to feed the container with a factory that can create the requested service.
The package should be able to declare that the service it provides is the default implementation for a given interface (this is important for auto-wiring containers).
My MyLogger class implements PSR-3. I want a way to tell the container explicitly that if a service
has a dependency on the Psr\Log\LoggerInterface, the entry I provide is a good candidate and can be
used.
Note: in almost all containers I've seen, this is done by relying on convention and "aliases". It seems there is an agreement that to "claim" ownership of an interface, your identifier for the entry should be the fully qualified name of the interface.
"Psr\Log\LoggerInterface" => your logger instanceSince a service can implement many interfaces, the best practice here seems to be using aliases.
"MyLogger" => your logger instance
"Psr\Log\LoggerInterface" => alias to "MyLogger"
The package can register a provided service as part of a "group" of services that share the same purpose.
Those services might need to be ordered inside the tag.
My package is providing a PSR-15 middleware in charge of transforming exceptions into HTTP 500 pages. I don't know who will consume it (it might be Zend Stratigility or any other PSR-15 consumer). I want my package to instruct the container that:
Note: PSR-11 does not introduce the notion of tags. Indeed, all containers do not have the notion of tag built-in. But PSR-11 specifies that a container entry can be anything (it is not limited to objects only). Therefore, a "tag" can be seen as a container entry that contains an array of services.
A package should be able to alter/modify a service stored in the container, either by calling methods on it or by "decorating" it.
My package is providing a Twig extension. I want my package to:
$twig->register($myExtension);.When you install a Symfony Bundle in Symfony 4 (via composer require), Symfony Flex takes care of registering the
bundle for you. Same thing with Laravel Service Providers since Laravel 5.5
or with Zend Framework.
Those frameworks are actually coming with a Composer plugin that triggers after a package is installed and that is
looking for some configuration in the package (either a manifest.json file or a special "extra" section in the
composer.json file).
If we want our package format to be as easy to use as the framework specific package formats, we need to have a way for underlying frameworks to perform auto-discovery of service providers.
I'm performing a composer require to install a new package. Some composer plugin specific to my
framework is triggered. The framework registers the service provider of my package automatically.
If a package has requirements (in the form of dependencies that need to be available in the container or in the form of required configuration), it should be able to publish those programmatically to the underlying framework.
I'm installing a database abstraction library (Doctrine DBAL for instance). The package creates a default database connection instance in the container. This instance requires a number of parameters (database driver, host, name, user, password, etc...). These parameters might not be available in my application yet. My framework is clever enough to notice this at install time (maybe via a Composer plugin) and asks me to fill the parameters.
I'm installing a library that performs heavy computation. This library absolutely needs a PSR-16 CacheInterface
to run. I don't have such an entry yet in my container. My framework notifies me that my container is in an
inconsistent state and that I need to install another package to provide this PSR-16 CacheInterface entry
(bonus point if the framework can propose a package that does this).
Based on configuration, a package should be able to register or not a set of entries.
I'm installing a database abstraction library (Doctrine DBAL for instance). For my application, I don't have one but 2 databases. I can add an array of connections in my configuration and the service provider can create in the container 2 separate connection entries.
That's it! That's my dream list of requirements for this PSR (this might actually span over several PSRs).
If we manage to agree upon such a cross-platform package format, I'm absolutely sure this might be absolutely beneficial to the PHP ecosystem.
The current proposal we are working on (container-interop/service-provider) does correctly cover features 1 to 4, but does not cover 5, 6 and 7. So my current thought is we should work towards filling those features. Do you see any feature that should be part of the PSR I missed? Or do you think the scope is too wide?
++
David
Twitter: @david_negrier
Github: @moufmouf