PSR-15 proposes a standard around PHP middleware that consumes PSR-7 HTTP message instances. One goal of the standard is to create interfaces that can co-exist with existing projects, which would allow adoption without necessarily leading to backwards compatibility breaks.
The most recent proposal has the following interfaces:
namespace Interop\Http\Middleware;
use PSR\Http\Message\RequestInterface;
use PSR\Http\Message\ResponseInterface;
use PSR\Http\Message\ServerRequestInterface;
interface DelegateInterface
{
public function process(RequestInterface $request) : ResponseInterface;
}
interface ServerMiddlewareInterface
{
public function process(ServerRequestInterface $request, DelegateInterface $delegate) : ResponseInterface;
}
interface MiddlewareInterface
{
public function process(RequestInterface $request, DelegateInterface $delegate) : ResponseInterface;
}
DelegateInterface
is roughly analogous to Zend\Stratigility\Next
, while
ServerMiddlewareInterface
is analogous to
Zend\Stratigility\MiddlewareInterface
.
We'd take a tiered approach, over a minor version and a following major version.
First, we can update Next
to implement DelegateInterface
. At first, this
would be adding a process()
method that proxies to __invoke()
, and making
the ResponseInterface
argument optional:
namespace Zend\Stratigility;
use Interop\Http\Middleware\DelegateInteface;
use PSR\Http\Message\RequestInterface;
use PSR\Http\Message\ResponseInterface;
use PSR\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response;
class Next implements DelegateInteface
{
public function __invoke(
ServerRequestInterface $request,
ResponseInterface $response = null,
$err = null
) {
/* ... */
}
public function process(RequestInterface $request)
{
return $this($request);
}
}
We'd also mark __invoke()
as deprecated via annotation.
For the next major version, we'd modify __invoke()
to proxy to process()
. Since
one suggested change for v2
includes removal of the $err
argument already, this version would also drop
the $response
argument, so that it now reads:
namespace Zend\Stratigility;
use Interop\Http\Middleware\DelegateInteface;
use PSR\Http\Message\RequestInterface;
use PSR\Http\Message\ResponseInterface;
use PSR\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response;
class Next implements DelegateInteface
{
public function __invoke(ServerRequestInterface $request)
{
return $this->next($request);
}
public function next(RequestInterface $request)
{
/* ... */
}
}
At the same time, we'd also mark the __invoke()
method as deprecated; we might
potentially also raise an E_USER_DEPRECATED
error within the __invoke()
method to encourage usage of the process()
method.
For a minor version:
- We'd update
MiddlewarePipe
to also allow PSR-15ServerMiddlewareInterface
andMiddlewareInterface
implementations. - We'd update
Dispatch
to check forServerMiddlewareInterface
andMiddlewareInterface
instances; if found, it would invoke them with only the passed request. - We'd mark
MiddlewareInterface
as deprecated. We'd add a class that can decorate callable middleware to be invoked as(See comment below for details on why this is now omitted.)ServerMiddlewareInterface
implementations. This would check the arity of the callable to ensure that it can be invoked per the PSR-15 specification.
For a major version:
- We'd remove
MiddlewareInterface
entirely. Next
would check if the handler is aServerMiddlewareInterface
implementation before dispatch (see the error handler proposal for details on why this goes inNext
and notDispatch
).- We'd update
MiddlewarePipe
to only allowServerMiddlewareInterface
andMiddlewareInterface
implementations.
PSR-15 is not yet accepted, and could still undergo revision, making it a moving target.
I've decided to move this functionality either into its own repository or include it as part of zend-expressive.
The reason is because this sort of wrapper will require a
Response
instance.To illustrate:
Note the comment inside the
process()
method: the second argument currently in Stratigility and Slim middleware is a response instance, but we do not have one!There are two ways to make this work:
MiddlewarePipe
, as it would require the pipeline to have one of these artifacts, which breaks SRP and the Law of Demeter.One further possibility that would make the second approach work is to create a "callable middleware decorator factory", which could be injected in a
MiddlewarePipe
in order to allow auto-wrapping of callable middleware:Interestingly, zend-expressive already requires zend-diactoros, which would solve the problems presented in the first bullet point. Additionally, I'd argue any auto-negotiation of arguments should likely be relegated to something built on top of
MiddlewarePipe
, similar to how Expressive adds support for pulling named middleware from a container (instead of requiring callable middleware). This may take the form of a new method (e.g.,pipeCallableMiddleware(callable $middleware)
), or just be inlined in thepipe()
method; either way, it is not the responsibility of Stratigility.As such, I'm omitting that task from this RfC.