A PSR is often comprised of text and code, generally interfaces. Those interfaces are pieces of code that are released and tagged at a specific moment in time. However, the PHP language doesn't stand still; it evolves over time.
This means that those interfaces need to both provide a stable contract, as well as evolve to leverage new language features that could help better enforce the behaviors described in the PSR itself.
At the same time, a PSR cannot be changed after its release (at which point only erratas are allowed), to protect a package that declared compatibility from becoming de facto incompatible.
This document defines a process to be followed in updating PSR interfaces, in a way that is not breaking in regard to behavior for end users, and with an appropriate upgrade path for the consumers.
A new minor release of a PHP-FIG package containing interfaces for a PSR MUST follow these rules:
A new major release of a PHP-FIG package containing interfaces for a PSR MUST follow the same rules, with this exception:
Note that if the upgrade path causes the consumers or implementers to maintain multiple versions of their libraries side-by-side, only to support multiple versions of the same PSR, the upgrade path is to be considered too steep.
Since releasing new versions of the interfaces MUST NOT alter the PSR in its behavior, those releases can be voted in with the same process as errata changes. The new releases MUST be declared and embedded with a brief explanation and a link in the PSR document, like in the following example:
```php interface ContainerInterface { // code snippet here } ```
Since psr/container version 1.1, the above interface has been updated to add argument type hints.
In the example above, the last line is indicative of what should be added to the specification.
The meta document MUST be amended with information detailing the consumer and/or implementer upgrade path.
A common case for an upgrade in the interfaces is to add types for parameters and returns, since they are new language features introduced by PHP 7, and many PSR interfaces predate that release. We'll use a method from PSR-11 as an example of how a PSR interface could be updated.
PSR-11 is released with the psr/container
package and it holds the ContainerInterface
, which has this method:
/**
* @param string $id Identifier of the entry to look for.
*
* @return bool
*/
public function has($id);
This method could be updated with a new minor release that adds the argument type for $id
:
public function has(string $id);
This change would technically be a breaking change, but thanks to the limited contravariance possible in PHP 7.2, we can avoid that. This means that just by requiring "php": "^7.2"
in the psr/container
composer.json
, we could tag this change as a minor release, and have all the consumers and implementers be automatically cross-compatible, provided that they declare "psr/container": "^1.0"
(or equivalent) as a constraint.
After this intermediate step, it would be possible to release a new major version, adding a return type hint:
public function has(string $id): bool;
This must be released as a new major version of psr/container
(2.0); any package that would implement this would be able to declare "psr/container": "^1.1 || ^2.0"
, since backward compatibility to the first release would be impossible, due to the sum of covariance and contravariance rules.
On the other side, the implementers would be able to do a release cycle in the opposite fashion. The first release looks like this:
public function has($id);
The second release would add the return type, maintaining compatibility with the original interface:
public function has($id): bool;
A third release would also add the argument type hint:
public function has(string $id): bool;