PSR-7 middleware

2679 views
Skip to first unread message

Rasmus Schultz

unread,
May 9, 2016, 4:00:38 AM5/9/16
to PHP Framework Interoperability Group
Dear FIG,

People often talk about "PSR-7 middleware", by which they mean middleware components designed to work with the PSR-7 Request/Response abstractions.

The problem is, PSR-7 does not define any interface for middleware, as such.

Thus, more or less every framework currently defines their own variation of (essenatially) this interface:


Sadly, that does not give us true interoperability at the middleware level - although it kinda sorta does, as most middleware stacks do not rely on type-checking this interface, and instead rely on developers to implement __invoke() or anonymous closure signatures correctly, which seems shaky.

What would it take to have this interface added to PSR-7?

Is amending a PSR even a thing? (It would not be a breaking change.)

Of course, we could propose a new PSR just for this, but people already talk of "PSR-7 middleware", and it's often hard to grok what they mean. If we add a proper interface for them to work with, "PSR-7 middleware" would actually be a thing :-)

Thoughts?

- Rasmus

Paul Jones

unread,
May 9, 2016, 9:01:54 AM5/9/16
to php...@googlegroups.com

> On May 9, 2016, at 03:00, Rasmus Schultz <ras...@mindplay.dk> wrote:
>
> Is amending a PSR even a thing? (It would not be a breaking change.)

It is, but my guess is this would be outside the scope of amendment, and require a new PSR.

> Of course, we could propose a new PSR just for this, but people already talk of "PSR-7 middleware", and it's often hard to grok what they mean. If we add a proper interface for them to work with, "PSR-7 middleware" would actually be a thing :-)

I'd say a new PSR would be needed. Calling something "PSR-{$next} compliant middleware" should be sufficient.

Glad to see someone talking about actual productive work, rather than parliamentary process.


--

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



Woody Gilk

unread,
May 9, 2016, 4:39:37 PM5/9/16
to PHP Framework Interoperability Group
Seems like this is a shoo-in for implementation. Will someone volunteer to start the process?



--
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/6E0D42E3-F11E-4F8F-A58B-A2F9917469B5%40gmail.com.
For more options, visit https://groups.google.com/d/optout.

Paul Jones

unread,
May 9, 2016, 6:13:06 PM5/9/16
to php...@googlegroups.com

> On May 9, 2016, at 15:38, Woody Gilk <woody...@gmail.com> wrote:
>
> Seems like this is a shoo-in for implementation.

One hopes.


> Will someone volunteer to start the process?

I'm talking about it with Rasmus even now.

Marco Perone

unread,
May 10, 2016, 2:41:25 AM5/10/16
to PHP Framework Interoperability Group
Some time ago there was some discussion on the same topic (https://groups.google.com/forum/#!searchin/php-fig/http$20handling/php-fig/Ew36Ng5EwXE/52dAzZAbAQAJ). That could be a starting point

Dracony

unread,
May 10, 2016, 3:04:15 AM5/10/16
to PHP Framework Interoperability Group
I'm really not a fan of interfacing __invoke and would like an actual method like handle() or process() instead.

The only upside of using __invoke is that you can pass a callback instead of an actual interface implementation and the result would still work. However if the Functional Interfaces RFC passes (which would be amazing btw: https://wiki.php.net/rfc/functional-interfaces ) the __invoke approach will be no longer needed, since we'll be able to implement interfaces with callbacks anyway.



On Monday, May 9, 2016 at 10:00:38 AM UTC+2, Rasmus Schultz wrote:

Stefano Torresi

unread,
May 10, 2016, 7:15:53 AM5/10/16
to php...@googlegroups.com
Let me ask something:


Il giorno mar 10 mag 2016 alle ore 09:04 Dracony <draco...@gmail.com> ha scritto:
I'm really not a fan of interfacing __invoke and would like an actual method like handle() or process() instead.

What are the upsides of an actual method vs __invoke() ?
 
The only upside of using __invoke is that you can pass a callback instead of an actual interface implementation and the result would still work.

What are the downsides of __invoke() then?

FWIW I have worked with Stratigility and ExpressJS, and the anonymous ($req, $res, callable $next = null) signature (also used by Relay) is staple, widely accepted, and without any apparent issue whatsoever.
This would also result in a standard of which two major vendors are already compliant.

Are there any compelling reasons against it, other than "meh, don't like"?

Dracony

unread,
May 10, 2016, 7:28:25 AM5/10/16
to PHP Framework Interoperability Group
Taking up a magic method somewhat limits the developers choice, since maybe they would like to use __invoke differently. With specific methods you're more safe there will be no collision when implementing multiple interfaces. The other thing is that __invoke doesn't really tell you what the method is doing and makes it clunkier to add other methods later on because of naming:

Imagine you had a method add($number), then you can also add a method addOne() which looks consistent.
Now if you used __invoke to add a number, you can't have __invokeOne, you still have to call the method addOne, which makes the __invoke one look weird.

The only upside of having __invoke is slight reduction in characters required to call the middleware, but the $middleware($request, $response) syntax might look weird to somebody who would expect $middleware->run($request, $response).

On Monday, May 9, 2016 at 10:00:38 AM UTC+2, Rasmus Schultz wrote:

Josh Di Fabio

unread,
May 10, 2016, 7:32:06 AM5/10/16
to php...@googlegroups.com
One argument for having an interface with a named method is that code becomes more explicit:

$middleware->handleRequest($request, $response);

vs

$middleware($request, $response);

Woody Gilk

unread,
May 10, 2016, 9:14:03 AM5/10/16
to PHP Framework Interoperability Group
How does that make the code more explicit? Not using __invoke makes using a closure impossible. To say that every framework that has already implemented this interface is wrong doesn't make any sense to me.

I've opened a PR to propose this interface for inclusion in FIG: https://github.com/php-fig/fig-standards/pull/755

--
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.

Dracony

unread,
May 10, 2016, 9:25:27 AM5/10/16
to PHP Framework Interoperability Group
Not impossible if the Functional Interfaces RFC gets through, which would be best.

And actually keeping the __invoke way just because there are people already using it, is not really convincing. I mean, PSR-7 came up with an interface nobody wsas using and people were forced to change. Nothing bad came out of it. 

Josh Di Fabio

unread,
May 10, 2016, 9:42:36 AM5/10/16
to php...@googlegroups.com
Functional interfaces are more explicit in the sense that the code describes what is happening, whereas __invoke doesn't. Without the functional interfaces RFC, which is currently under discussion, I agree that __invoke is probably a better approach, but I think it makes sense to wait for that RFC to undergo a vote before deciding which approach to take, even though functional interfaces would presumably be a PHP7-only feature.

Oscar Otero

unread,
May 10, 2016, 12:05:30 PM5/10/16
to php...@googlegroups.com
I like this proposal and I’m agree with using __invoke because it’s more flexible and allows to use callables for simple purposes.

I’d like also to take the opportunity to talk about an issue I found in the psr-7 middleware.

Currently, psr-7 includes two types of request: RequestInterface and ServerRequestInterface. The first one should be used for client side and the second one for server side.
The initial proposal of Woody Gilk (https://github.com/php-fig/fig-standards/pull/755) uses ServerRequestInterface so this interface cannot be used in client side applications. There are many middleware pieces that could be used in both sides, no matter whether the request is send as a client or a server, for example, logging, minify, cache, etc. There’s a discussion about this in relayphp: https://github.com/relayphp/Relay.Relay/issues/5#issuecomment-112548669

In other hand, some middleware pieces require to save/retrieve attributes, only available in ServerRequestInterface. This means that some middlewares that could be used in both contexts (client and server side), only can be used in server side due the lack of attributes in client side. (maybe this is an issue of psr-7, not allowing to store attributes in RequestInterface instances?)

So a possible solution should be to have two interfaces, a MiddlewareInterface (using a RequestInterface) and a ServerMiddlewareInterface (using the ServerRequestInterface). But I don’t like it because the first one cannot be extended by the second one (as it happens with request and serverRequest) so type checking is complicated.

I don’t know what can be the solution (or maybe you don’t see a problem here).

Regards
Oscar Otero.



Paul Jones

unread,
May 10, 2016, 12:23:13 PM5/10/16
to php...@googlegroups.com

> On May 10, 2016, at 11:05, Oscar Otero <oscar...@gmail.com> wrote:
>
> I don’t know what can be the solution (or maybe you don’t see a problem here).

I've said elsewhere that one solution might be two interfaces: a ClientMiddleware that typehints on RequestInterface, and a ServerMiddleware that typehints on ServerRequestInterface. (That's the current plan for Relay 2.x, anyway.)

Woody Gilk

unread,
May 10, 2016, 12:24:27 PM5/10/16
to PHP Framework Interoperability Group
Oscar,

I'm not entirely sure what you mean by a "client side application". Can you provide an example where a ServerRequest would not be the appropriate type? I'm not really sure what scenario middleware would be used in that wasn't a server.

Oscar Otero

unread,
May 10, 2016, 12:32:11 PM5/10/16
to php...@googlegroups.com
Any app that send a http request to other server and get a response. For example to consume an API, download files, etc. In this case we don’t need a ServerRequest. And we may want to use a middleware to save the responses in files, loggin, strip the trailing slash of the request’s uri, etc…
The middleware logic is the same than server side apps, the only difference is in we are using curl, guzzle or any other similar library instead routers and controllers.

Woody Gilk

unread,
May 10, 2016, 12:35:08 PM5/10/16
to PHP Framework Interoperability Group
Ah, so something like a Guzzle middleware stack that (for example) caches responses to an external API?

Woody Gilk

unread,
May 10, 2016, 12:37:48 PM5/10/16
to PHP Framework Interoperability Group
I see two possibilities:

1. Use a different namespace, with the same interface name: Psr\Http\ServerMiddleware vs Psr\Http\ClientMiddleware
2. Change the Request requirement to be non-server and add a new method call that determines if the middleware requires a ServerRequest, something like: isServerOnly()

Thoughts?

Oscar Otero

unread,
May 10, 2016, 1:04:43 PM5/10/16
to php...@googlegroups.com
The problem I see with the type checking is the following:
Let’s say we have a middleware dispatcher library with the following interface:

<?php
Psr\Http\ServerMiddlewareInterface;

interface MiddlewareDispatcher
{
    public function add(ServerMiddlewareInterface $middleware);
}

This interface cannot accept also ClientMiddlewareInterface instances, because ServerMiddlewareInterface cannot extend ClientMiddlewareInterface due both interfaces use the same method (__invoke). So we are creating two different incompatible worlds.

The second possibility solves this problem, but it does not allow to use closures.



Woody Gilk

unread,
May 10, 2016, 1:13:31 PM5/10/16
to PHP Framework Interoperability Group
Alright, then I would suggest that the interface be as flexible as possible and that any middleware the requires a ServerRequest do something like:


public function __invoke(...)
{
    $this->ensureServerRequest($request);
}

private function ensureServerRequest(ServerRequestInterface $request)
{
    return $request;
}


In this manner the interface can be usable for both client and server requests, and PHP runtime checking will ensure that a ServerRequest is used when appropriate. The ensureServerRequest() method could be provided by a trait in the PSR package, which would make it really easy:


class AcmeServerMiddleware implements MiddlewareInterface
{
    use EnsureServerRequestTrait;

    // __invoke will call ensureServerRequest($request) as necessary
}


Would that be an acceptable compromise?

Roman Tsjupa

unread,
May 10, 2016, 1:19:08 PM5/10/16
to PHP FIG

Exactly the problem I described earlier, there is only one invoke() and that limits you. While with normal methods you coukd have handleRequest and handleServerRequest() methods without conflict

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/vTtGxdIuBX8/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.

Woody Gilk

unread,
May 10, 2016, 1:22:53 PM5/10/16
to PHP Framework Interoperability Group
Roman, having two methods does not "resolve" the situation.

Last time I checked PHPixie didn't even have middleware, so what grounds are you basing your opinions on?

Oscar Otero

unread,
May 10, 2016, 1:36:21 PM5/10/16
to php...@googlegroups.com
I like this approach. Maybe throwing a RequiredServerRequestException ?


Roman Tsjupa

unread,
May 10, 2016, 1:37:54 PM5/10/16
to PHP FIG

It gas middleware embodied in processors. That allows for a declarative approach to request processing. Having separate methods allows both methids to be sarisfied and look coherently imo

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/vTtGxdIuBX8/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.

Woody Gilk

unread,
May 10, 2016, 1:39:31 PM5/10/16
to PHP Framework Interoperability Group
In the example I provided, PHP would throw a type error if a Request was used instead of a ServerRequest. The proposed method could also just be anonymous and throw an exception:


private function ensureServerRequest(RequestInterface $request)
{
    if (($request instanceof ServerRequestInterface) === false) {
        throw RequiredServerRequestException();
    }
}

Tristan Darricau

unread,
May 10, 2016, 1:49:51 PM5/10/16
to PHP FIG

About the initial proposal I don't see the point of adding an interface with _invoke(). When I'm using _invoke() it's to be able to pass a closure. But when I'm introducing an interface it's to type hint against it, which prevent me from using a Closure if I'm not using php 7.1 or an higher version...


Rasmus Schultz

unread,
May 10, 2016, 2:32:29 PM5/10/16
to php...@googlegroups.com
Just catching up on all these emails, and I'm going to
comment on several issues brought up since my original post.

On the discussion about __invoke() vs a named method - this
is a radical departure from what anyone (framework or
middleware vendors) are doing now. Effectively, nothing will
work out of the box. It also breaks the very common pattern
where one simply passes a closure.

The upshot of course is strict, checkable middleware
signatures. And we don't need to wait for functional
interfaces for that either, it's possible now, with
anonymous classes, in PHP 7:

interface MiddlewareDelegateInterface {
public function run(
RequestInterface $request,
ResponseInterface $response,
MiddlewareDelegate $next
) : ResponseInterface;
}

interface MiddlewareInterface {
public function run(
RequestInterface $request,
ResponseInterface $response,
MiddlewareDelegate $next
) : ResponseInterface;
}

class Dispatcher {
// ...

public function push(MiddlewareInterface $middleware) {
// ...
}
}

$dispatcher = new Dispatcher();

$dispatcher->push(new class () implements MiddlewareInterface {
public function run(
RequestInterface $request,
ResponseInterface $response,
MiddlewareDelegate $next
) : ResponseInterface;
{
// ...
}
});

Due to the sheer verbosity and limited gain, I don't think
I'm in favor.

I'm not 100% sure, because I generally am in favor of
anything that is type-safe and supports static analysis,
provides IDE support, etc. - but it seems like a pricey
trade-off, and everyone seems pretty happy with the
widely-used approach to middleware and middleware stacks
that is already in popular. It hasn't caused me any real
problems.

So I think I'm in favor of standardizing on something that
is closer to what the community already large agrees upon,
which can be implemented in very little time, in some cases
by simply proofreading existing code and asserting that it
already complies, and/or swapping out a userland interface
for the PSR interface.

The argument about occupying the magic `__invoke()` method
is somewhat esoteric to me - if you're writing a middleware
component, it seems pretty unlikely your class is going to
have other responsibilities that require it to implement
`__invoke()` for other reasons. I agree with Woody on this
one - frameworks are already succeeding with that approach,
and the anonymous classes approach would be lumpy and
precludes any project targeting PHP < 7, which likely just
means it wouldn't get implemented or used at all.

I think we should design for the real world, e.g. to help
provide formal interoperability of software components
already present in the landscape.

I would add also that standardizing on an HTTP middleware
interface that is largely compatible with most existing
middleware and stacks today, does not preclude us from
proposing a more modern standard in the future if/when
something like functional interface comes around and becomes
commonplace. Right now, it's a bit of a fantasy.

On the ClientMiddleware vs ServerMiddleware debate, I don't
have much to add. I think that we need two interfaces -
possibly two sets of interfaces, if we decide to formally
define an interface for the `$next` middleware delegate
function, which I think we should.

Would it make sense to separate the client and server
middleware standard completely, as two different standards
and packages? A lot of projects are going to need/support
only the server middleware interfaces - and most actual
middleware components are going to need only either the
client or server interface, rarely both. In fact, Guzzle is
probably at this time (one of) the only middleware hosts
that need the client middleware interfaces at all?

Finally, re: Tristan Darricau

> About the initial proposal I don't see the point of
> adding an interface with _invoke()

In many cases, we type-hint as `callable` in method-
signatures, and type-hint statically with a php-doc tag,
e.g.:

/**
* @param callable|MiddlewareInterface
*/
public functon push(callable $middleware) {
// ...
}

It doesn't provide run-time safety, but you can implement
that with run-time checks and throw an exception in
middleware hosts. It serves primarily as documentation
in this case.

And of course it serves it's primary purpose when middleware
classes implement it, ensuring that the call signature of
middleware components is correct.

That's all I have at this time.

For the record, I'm not eager to champion this effort - I
have too many side projects as it is, so if Woody wants
to take the lead, I am fine with that :-)
> https://groups.google.com/d/msgid/php-fig/CACd2v47exVvQJJ_rhWwCnRxyMB7toWZLsqx8fTGaEAoEfS4Qqw%40mail.gmail.com.