PSR-7 middleware

2,715 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.

Rasmus Schultz

unread,
May 10, 2016, 2:45:18 PM5/10/16
to php...@googlegroups.com
Oh and, er, here's my draft for the PSR and server interfaces:

https://gist.github.com/mindplay-dk/1d60ccfa083acfca32698d3c6d9a0945

Rasmus Schultz

unread,
May 10, 2016, 2:53:50 PM5/10/16
to php...@googlegroups.com
Oh, just one thing I'd like to add.

We can take a hint from virtually every middleware stack in the PHP
sphere right now and comply with what is already widely used and
already widely referred to as "PSR-7 middleware" - and deliver a PSR
that is useful to the community, now, today.

Or we can struggle to come up with something "better" that won't be
approved before at least a year, and won't be implemented for years to
come.

Or, hey, we can do both! Deliver a PSR that is useful today and can be
adopted with minimum fuss - and continue working on a modern
alternative to replace it some years from now.

I don't think there'a any reason not to do the first, since it's
already happening informally with or without this PSR, regardless of
what anybody "wants" - it's not perfect, but the language doesn't
really seem to support a "best of all worlds" middleware concept at
this time.

IMO, the community needs this PSR now - or two years ago ;-)

Woody Gilk

unread,
May 10, 2016, 2:58:00 PM5/10/16
to PHP Framework Interoperability Group
On Tue, May 10, 2016 at 1:53 PM, Rasmus Schultz <ras...@mindplay.dk> wrote:
>
> Or, hey, we can do both! Deliver a PSR that is useful today and can be
> adopted with minimum fuss - and continue working on a modern
> alternative to replace it some years from now.


Eloquently put. I agree completely, which is exactly why I think the
current proposal is the right way to go. We can certainly do a 2.0 or
a totally new PSR with a PHP 7.1+ approach that has return type hints
and rainbows. For now, let's just standardize something that is
already de facto standard.

Oscar Otero

unread,
May 10, 2016, 3:09:09 PM5/10/16
to php...@googlegroups.com
I’d like to add a real example about separate both server/client middleware components.

I’m using this component to save the responses into files:

This allows to generate a html file with the first request and serve it as static file the next requests. In other words, a file base caching (or extreme caching as it’s named here: http://mateusztymek.pl/blog/extreme-caching-psr7)

The signature of the function use the RequestInterface, so it can be used also in a client-side app.

If the interface is divided in two, it’s needed to create two classes for the same purpose.


Paul Jones

unread,
May 10, 2016, 3:21:30 PM5/10/16
to php...@googlegroups.com
Agreed on all counts.

Ross Tuck

unread,
May 10, 2016, 3:36:06 PM5/10/16
to PHP Framework Interoperability Group
Hi folks,

Just a few thoughts, based on years of wanting this and a couple of conversations today:

a) With regards to __invoke vs a named method: there is some legit strangeness to having an interface that requires a callable for another instance, but we can't typehint it properly. I've seen this cause some confusion in Tactician as well, which operates on the same principle. If we change it to a Middleware typehint, we make it pretty clear what's happening.

b) However, that doesn't mean we have to give up closures, which I love. Instead, it would be a couple lines of code for dispatchers to ship with a "new CallableMiddleware(function (...) {})" that just wraps a callable. That's uglier syntax but the dispatcher could just do the wrapping internally with a little syntactic sugar. So, a dispatcher's add() takes a Middleware instance which it adds directly to the stack or a callable which it wraps and then adds it to the stack. If you want strong typehint, okay, add() vs addCallable() It's a small, easy thing for dispatchers to do (for the ones that want to!)

c) Furthermore, I don't think we should be trying to standardize a dispatcher interface in anyway. The practices are still being worked out, the idomatic path is going to be tooling specific and folks are going to be wiring up their dispatchers directly, so there's no point trying to standardize them. Plus, I don't really see any gain compared to standardizing the middleware itself, which is where the reuse love is.

d) Finally, I had a conversation with Anthony Ferrera earlier today, where he brought up an interesting point: why do we have the Response added in as a parameter? Why can't it be solely a return value? At first, this seemed pretty common sense to me (it's what's in use more broadly) but when we started working through the examples, I realized that PHP's HTTP signature differs in a key way from most other languages: it's immutable. The only Response instances that really count are the ones returned from the app or the error handling middleware, both of which are returned. In most other languages, you're carrying the Response forward so you can write to it but that's just not true in PSR-7. I've looked at a few other use cases like Cache-Control, ETags, Auth, Routing, etc and I just don't see a use case in which passing it forward in the chain really helps (especially since you can just call the next middleware at the start of your middleware to get an instance to write to). I was a bit surprised at first, but the more I look at this, the more it makes sense. If anyone has any use cases that they can't make work otherwise, please let me know and I'll see if they can be adapted.

With regards to other points, I believe we were roughly in agreement with a signature something like "handle(Request $request, Middleware $next): Response" I know that's a bit different but from what's out there now, but as I mentioned, I think we're doing a couple things differently in PSR-7 already, there can be an easy conversion layer and it's worth having a conversation about before Middleware hit the big time everywhere.

Please keep in mind, it's a separate point from the ones raised above, which I think are valid on their own.

Finally, it's good to see folks are ready for this discussion. I believe standardized middleware could be one of the most important architectural shifts in PHP for the foreseeable future and sticking the landing on this one, both technically and marketing wise would be awesome.

Thanks!

Woody Gilk

unread,
May 10, 2016, 3:46:46 PM5/10/16
to PHP Framework Interoperability Group
On Tue, May 10, 2016 at 2:36 PM, Ross Tuck <m...@rosstuck.com> wrote:
> why do we have the Response added in as a parameter? Why can't it be solely
> a return value?

I can answer this very simply: Dependency inversion does not allow it.

If your middleware is only ever type hinted against PSR-7
request/response interfaces (as it should be) then by what mechanism
do you create a response? How do you know what vendor is going to
provide the expected implementation? How do you instantiate the
response class correctly?

The short answer is that you don't and you shouldn't. The correct
thing to do is invert the dependencies and create the request and
response objects early on in application execution and pass them
through the stack:

dispatch(request, response) : response

In this way, you can be reasonably sure that the final response is
going to be the same implementation (but most likely not the same
instance, due to immutability) as the response that was initialized
early on.

Ross Tuck

unread,
May 10, 2016, 4:19:14 PM5/10/16
to PHP Framework Interoperability Group
With respect, I'm not sure Dependency Inversion is a complete reason to inject the response here.

Even if we do inject the Response, they're essentially value objects in the HTTP scope and it would be exceedingly common for someone to just return a different instance, rather than modify the instance passed in. I'd go so far as to call this a feature for many use cases: if I'm writing an ETag middleware and want to return a cache hit, I don't want to manually truncate the body and clear other headers off when it would be easier to just return a new response and to my knowledge, PSR-7 doesn't have a "createBlankInstance" factory method.

Considering how easy (and occasionally reasonable) it is to return a new instance within any given middleware, I don't think including a response provides _any_ guarantee about the return value and we should bake that assumption into the spec recommendations from the outset, lest we end up with a bunch of soft dependencies or LSP violatons. if a dispatcher/framework needs a particular type of object internally, it would be better to convert to/from PSR-7 objects before/after the middleware dispatcher cycle, rather than assume it's going to get the same type at the end. "My\Special\Response::fromPsr7Response($response);"

Just my 2 cents, tapping out for the night. :)

Rasmus Schultz

unread,
May 10, 2016, 4:19:29 PM5/10/16
to php...@googlegroups.com
I strongly agree with your points about not standardizing dispatch -
it provides no real benefits; interoperable middleware should be the
primary objective.
> --
> 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.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/2cc9f582-c311-483b-9266-e82638d25fd4%40googlegroups.com.

Roman Tsjupa

unread,
May 10, 2016, 4:25:23 PM5/10/16
to PHP FIG

There is really no point in standardizing dispatchers since you dont expect one dispatcher to be portable with other framework/library. Even then it is possible for the dispatcher to just implement the middleware interface and wrap the middlewares inside it.

Woody Gilk

unread,
May 10, 2016, 4:33:29 PM5/10/16
to PHP Framework Interoperability Group
On Tue, May 10, 2016 at 3:19 PM, Ross Tuck <m...@rosstuck.com> wrote:
> if I'm writing an ETag middleware and want to return a cache hit, I don't
> want to manually truncate the body and clear other headers off when it would
> be easier to just return a new response and to my knowledge, PSR-7 doesn't
> have a "createBlankInstance" factory method.

This argument doesn't make sense to me, because there should be no
response body if you are going to return a cached response. Ordering
the middleware in such a way that the middleware queue makes sense and can
terminate early is up to the user. In the case of an ETag, the
implementation should be something along the lines of:


if (!$this->hasValidEtag($request)) {
$response = $next($request, $response);
}

$response = $this->setEtag($request, $response);

return $response;


A caching middleware should only continue to process the queue when
caching has failed, so that generating a complete response body can be
skipped entirely. This also highlights why the response is part of the
signature... you don't need to create a response because the existing
response is perfectly acceptable, it just needs the right headers.

Marco Perone

unread,
May 10, 2016, 5:03:45 PM5/10/16
to PHP Framework Interoperability Group
Consider the following case, using the signature you are suggesting

public function __invoke(Request $request, callable $next)
{
    if ($this->goOn()) {
        return $next($request);
    }

    // return a default Response
    return new MyResponseImplementation();
}

If I don't have a $request passed in as a method argument, I must depend on a concrete implementation of the ResponseInterface.

On the other hand, if a $response is passed in as an argument, I have a prototypical Response I can modify, not relying on any concrete implementation.

Michael Dowling

unread,
May 10, 2016, 8:33:00 PM5/10/16
to php...@googlegroups.com
One idea to work around this is to have a response factory injected into the middleware constructor/higher order function that could create a response if needed (which is an implementation detail of the middleware). That would allow a cleaner contract on the middleware (no need to pass in a prototype), but the caveat is that there would need to be a response factory interface.

Woody Gilk

unread,
May 11, 2016, 10:38:56 AM5/11/16
to PHP Framework Interoperability Group
https://github.com/php-fig/fig-standards/pull/755 has been updated
with a specification for server request checking using a trait and
exception. Please check it out and let me know if there are concerns.
On Tue, May 10, 2016 at 7:32 PM, Michael Dowling <mtdo...@gmail.com> wrote:
> One idea to work around this is to have a response factory injected into the middleware constructor/higher order function that could create a response if needed (which is an implementation detail of the middleware). That would allow a cleaner contract on the middleware (no need to pass in a prototype), but the caveat is that there would need to be a response factory interface.
>
> --
> 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/CD2802AB-8D04-49B6-9C25-E889D45095D1%40gmail.com.

Stefano Torresi

unread,
May 11, 2016, 11:18:43 AM5/11/16
to PHP Framework Interoperability Group

I would suggest renaming the exception static method to 'requiredBy', to make more clear why the middleware is passed as an argument.


Woody Gilk

unread,
May 11, 2016, 11:22:38 AM5/11/16
to PHP Framework Interoperability Group

Oscar Otero

unread,
May 11, 2016, 11:29:47 AM5/11/16
to php...@googlegroups.com
psr/http-message is php>=5.3 compatible, so the use of a trait in this proposal makes this package not fully compatible with it.
What do you think about providing a static method instead? (I don’t use php 5.3 but maybe other people do)
Or we can leave the trait, because is not part of the interface, and php 5.3 libraries can create it’s own helper.
> To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CAGOJM6LUOMGgjzHad5-Tp47LsMt4NM0SJLheBCMOpxdeM_gjaQ%40mail.gmail.com.

Woody Gilk

unread,
May 11, 2016, 11:38:30 AM5/11/16
to PHP Framework Interoperability Group
I would rather leave the trait as-is and let users of PHP <5.4
implement their own version of the check. The current proposal does
not require the usage of a trait, only offers it to ease
implementation.
> To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/B5498C0C-DC4C-48FC-BC11-45B8241072E0%40gmail.com.

Korvin Szanto

unread,
May 11, 2016, 11:43:21 AM5/11/16
to php...@googlegroups.com
I worked on my own middleware enabled psr-7 request handler for awhile and I went through all these oscillations we are talking about here. I'm typing this up on a phone so bear with me.

Firstly I ran into the attribute issue and asked around and it sounded like it was just an oversight. Another issue I ran into was the fact that there is no way to see if an uploaded file has been moved previously. We throw an ambiguous exception when the file has been previously moved and it's as far as I am aware impossible to properly handle the error case. Perhaps this is a good argument for a new psr-7 updated replacement.

On __invoke vs a properly named handler method, the reason I opted for a named handler method in the beginning is because it felt more natural to have my dispatcher typehint on my middleware interface rather than hinting on callable. I eventually conceded to using __invoke and hinting on callable due to pressure from other implementations. Because of this, I just opted to hint on callable and use closures instead of a real interface implementation [1].
See my dispatcher [2] which uses my overly complex pipeline [3] to send the request and response through a stack of middleware.

I recognize that it's more flexible but it really does feel wrong to me for reasons I cannot articulate. I honestly would much rather we have a named method for handling instead of using the magic method.

I 100% agree that both request and response need to be passed into the handler method. Passing in both allows you to choose whether you will return a new instance or will modify the running instance. Simply using a factory method or creating a new response every time just doesn't do it in my opinion. Though those options are still available if the response is passed in.

[1] https://github.com/Buttress/framework/blob/master/test/Test/Http/RequestHandlerTest.php
[2] https://github.com/Buttress/Http/blob/master/RequestHandler.php
[3] https://github.com/Buttress/Pipeline/blob/master/Pipeline.php

Thank you everyone for the discussion, it's a much needed distraction.
Korvin



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

Ross Tuck

unread,
May 11, 2016, 4:28:27 PM5/11/16
to PHP Framework Interoperability Group

Apologies for the novel below.


@Woody Putting the off-the-cuff Etag example aside, the body is less of a problem than the headers. You can easily replace the body of a response but PSR-7 has no good way to replace all headers, short of deliberately omitting all of the ones you can think of.


Imagine this example instead: You're building a middleware that catches exceptions and renders an error page when one occurs. When there's no exceptions, you just return the received response. Easy, works great! :)


Except one day you're debugging a page, the request goes all the way to the app, some header gets set in the controller but then something happens and an exception is thrown. The error middleware catches it, changes the body to the error page and then returns the response. And then suddenly the error won't go away because the Cache-Control header the app set wasn't cleared by the Error middleware. Or it tries to download the error page because the Content-Disposition was set. Or it works fine locally but not in production because it remembered to remove max-age but not s-maxage.


There's all sorts of stuff like this that could be avoided by either a) resetting all headers or b) returning a new instance. As mentioned, PSR-7 doesn't have a good way to void all headers, it can only remove a specifically mentioned header, one at a time. Short of writing a white-list-copy method, it seems much easier to just return a new blank instance.


So, this isn't an issue that effects all (or even most) middleware but it really makes life hard for those that it does hit.


Still, there's resistance to allowing middleware to create new instances, mainly because (and I'm interpreting) folks are worried about middlewares including bloat via alternate PSR-7 implementations or having conflicting version requirements. As it stands, there are three proposals to fix this:


1) We just allow them to include these alternate implementations. It's all the same interface and there's only a handful of implementations on the market.

2) Michael Dowling suggests a ResponseFactory that can be injected via constructor. This is a really good technical solution (though I'd recommend it include Request as well). My concern is that it will make the PSR seem more complex and we won't be able to teach folks to use it properly and they'll return new instances anyways.

3) Anthony Ferrera suggests a compromise: inject a blank/default response (or request) into the constructor. This is pretty simple, doesn't require any extra interfaces, plays well with their immutable nature and still lets folks configure everything by just using their DI containers. And it only effects those special middleware that need this. :)


Personally, I think #3 is a winner, both technically and in terms of a compromise that people can accept/teach. The constructor interfaces on the middleware will enforce good practice.


This might seem to render it a non-issue but there's still one underlying issue: We can't make anyone do this with their custom middleware. It is _really_ important that we recommend treating all PSR-7 implementations as equal and the dispatchers shouldn't count on getting the same concrete type back. It's just not enforceable with PHP's type system. If a dispatcher really needs its custom type, we should recommend they convert it back, never assume.


I can take or leave removing the Response parameter, that's not a huge deal to me, but treating the implementations as interchangable is really important.


One last thing and then I'll shut up. :) With regards to the client/server middleware and the trait accompanying it, I haven't seen much client middleware out there and I wonder if trying to capture that will make this PSR more complex (when it should be a really simple slam dunk).


What would folks think about moving Client middleware to a separate PSR and keep the scope here on just server middlewares, which have a clear, pressing need?


That would also seem to be a +1 for the named method argument, since we could release a method that typehints on ServerRequestInterface, then later release another PSR for client middleware. If your middleware supports both, cool, just implement two interfaces :) (There's a caveat here with the ServerRequest extending Request, but I have a couple ideas about that)

Woody Gilk

unread,
May 11, 2016, 4:41:26 PM5/11/16
to PHP Framework Interoperability Group
There is actually a simple way to remove all headers:


$headers = array_keys($response->getHeaders());

foreach ($headers as $header) {
$response = $response->withoutHeader($header);
}


This might not be the most elegant thing ever, but it is simple
enough. There are also other user-land ways to create an entirely
clean Response object. Basically, I don't think there is a strong
enough argument or enough real-world examples to make this a valid
concern.

As per your example of an error being cached, normally a Cache-Control
header would be set on output as part of a middleware. By this point,
the entire request would have successfully executed and the only place
an exception could be occurring would be in middleware itself. While
this may be a remote possibility, the likelihood in a production
application seems very remote to me.

There is also nothing in the current proposal that prevents having a
RequestFactory or ResponseFactory injected into middleware that needs
it. If there was such a need, it could be done as:


if ($this->whateverCondition($request, $response)) {
$response = $this->responseFactory->make();
}

// ... keep calm and carry on


Again, I think the current proposal does not need to address this
scenario because it is an edge case that can be addressed as necessary
in specific implementations.
> https://groups.google.com/d/msgid/php-fig/335ac8c1-aaa4-4409-b039-2bc50fac85c9%40googlegroups.com.

Paul Jones

unread,
May 11, 2016, 4:56:31 PM5/11/16
to php...@googlegroups.com

> On May 11, 2016, at 15:40, Woody Gilk <woody...@gmail.com> wrote:
>
> There is also nothing in the current proposal that prevents having a
> RequestFactory or ResponseFactory injected into middleware that needs
> it.

Exactly.


> Again, I think the current proposal does not need to address this
> scenario because it is an edge case that can be addressed as necessary
> in specific implementations.

Agreed.

Woody Gilk

unread,
May 17, 2016, 9:40:07 AM5/17/16
to PHP Framework Interoperability Group
The current proposal at
https://github.com/php-fig/fig-standards/pull/755 is still seeking a
second sponsor. An entrance vote cannot be started until then. Would
anyone be willing to put their name on it?

Matthew Weier O'Phinney / Zend Framework seems like an ideal candidate
to me, but so far he has not committed. Since it was raised for
discussion within The PHP League, perhaps Kayla Daniels would consider
being a co-sponsor?

The lack of movement on this amid all the other distractions has been
frustrating to me, because this should be an easy one to knock out of
the park. Pretty much everyone is already using some form of this
interface and I think it would look good to the community if we could
show some progress on actual standards.

Regards,
> --
> 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/37DA31C8-F456-4A7A-AC6F-8C498D1A3A94%40gmail.com.

Jason Coward

unread,
May 17, 2016, 11:06:46 AM5/17/16
to PHP Framework Interoperability Group
Woody:

Hello. As a representative of one of the projects this would formalize the existing HTTP middleware implementation for, I would like to volunteer to sponsor this proposal. I also would like to see more movement on standards-related activity. Thanks for getting the ball rolling on this and I look forward to doing whatever I can to help keep momentum going on this effort.

Cheers!

Jason Coward | Slim Framework

Woody Gilk

unread,
May 17, 2016, 11:48:44 AM5/17/16
to PHP Framework Interoperability Group
Jason,

Thank you for agreeing to sponsor! I have updated the PR accordingly
and Paul may start the entrance vote when he is ready.

Regards,
--
Woody Gilk
http://about.me/shadowhand


> https://groups.google.com/d/msgid/php-fig/8b8c3e2c-dd3f-4946-ae25-8a3074cf5697%40googlegroups.com.

Larry Garfield

unread,
May 18, 2016, 5:15:11 AM5/18/16
to php...@googlegroups.com
An important consideration is that while double-pass style (passing a
request and a response all the way through the pipeline) may be the most
popular approach by number of distinct implementations right now, by
number of installations using it the lambda-style (request in, response
out) is far, FAR more widely used. Although Fabien has openly said he
doesn't like the middleware approach, the Symfony HttpKernelInterface is
now at the heart of dozens of projects (including several members
here)[1]. If we're looking to standardize middlewares we should not
ignore that, prior to the last 12 months, HttpKernelInterface was the de
facto PHP middleware architecture.

[1] http://symfony.com/components/HttpKernel

--Larry Garfield

Glenn Eggleton

unread,
May 18, 2016, 8:19:44 AM5/18/16
to PHP Framework Interoperability Group
What exactly are you saying? We should include a Request In/Response out Middleware? 
That's fine, we should make another PSR Proposal for it.

There's nowhere saying we can't have more than one middleware PSRs, this one standardizes the Request In, Response In which many popular frameworks are using.

Rasmus Schultz

unread,
May 18, 2016, 8:33:03 AM5/18/16
to php...@googlegroups.com
Lots of middleware and dispatchers already rely on the ability to pass
and mutate both the request and the response - taking that away by
standardizing on a less capable interface doesn't make any sense,
regardless of who was first or what is/was popular.

IMO that shouldn't factor into this - and I don't think Symfony really
needs any special consideration here.

You can after all quite easily write an adapter for the Symfony
middleware approach by simply generating an empty response.

The opposite is *not* true - if the standard interface prevents you
from passing both request and response, there's no work-around.
> 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.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/573C3297.6040505%40garfieldtech.com.

Taylor Otwell

unread,
May 18, 2016, 9:29:40 AM5/18/16
to PHP Framework Interoperability Group
Passing a response into a middleware so you can decorate it before it hits your application is the most backwards concept I've seen on here in a while. Laravel's HTTP stack has been driven by middleware at its core for over 1.5 years (originally on StackPHP) and never has there ever been a situation where we needed to inject a response into the middleware. Nor has it ever been requested by a community member over those 1.5 years.

The dependency inversion argument doesn't make sense either. If a middleware in the middle of the chain needs to return a response, you just do "return new Response('Content', 200);" That is **entirely testable** because you simply write a test to examine the returned Response object. It's literally one of the easiest things to test. I'm not sure where all this "but we would have to inject a factory!" nonsense is coming from. Doesn't make sense. It is *OK* to use the "new" operator to create a Response instance in a middleware. It's perfectly testable.

Woody Gilk

unread,
May 18, 2016, 9:47:27 AM5/18/16
to PHP Framework Interoperability Group

Taylor, your entire argument is couched in terms that only make sense with a single implementation of request/response. Stack PHP is very specific to Symfony. In the world of PSR-7, a middleware _will not know_ what the correct implementation is. A middleware would depend on the interfaces _only_ and not a specific vendor package.

I don't understand why you are arguing so hard against something that most adopters of PSR-7 have already agreed upon informally.



For more options, visit https://groups.google.com/d/optout.
--

Rasmus Schultz

unread,
May 18, 2016, 9:54:31 AM5/18/16
to php...@googlegroups.com
I disagree that there is no dependency issue.

If the response object isn't injected to the middleware, every
middleware will have to depend on a specific implementation of the
PSR-7 response model. At the moment, they only need to depend on PSR-7
and not on any specific implementation. If middleware vendors started
selecting the response model implementation on their own, we would
quickly have a situation where a full stack of middleware ends up
using multiple different implementations of PSR-7, which creates a
performance issue.

In addition, as a middleware developer, I'm actually more comfortable
having both the request/response constructed for me - there's no
reason to burden middleware developers with selecting a PSR-7
implementation and constructing the response themselves.

I also disagree that injecting the response isn't useful. What about
something like a custom session middleware? It needs to sit at the top
of the stack to pre-populate the response with the required cookie
header. The response then proceeds to e.g. router-middleware etc.
where the remaining response it built up.

I'm sure there are other cases, like authentication headers etc. which
cannot be implemented this way.

Perhaps the most critical issue however, is (as said) the fact that
e.g. Laravel and Symfony can *easily* adapt to this interface - other
dispatchers can not adapt to a less capable interface without losing
this feature.
> https://groups.google.com/d/msgid/php-fig/1ad1f451-fc4d-4ee0-94eb-82ae09be0444%40googlegroups.com.

Rasmus Schultz

unread,
May 18, 2016, 9:57:05 AM5/18/16
to php...@googlegroups.com
> Taylor, your entire argument is couched in terms that only make sense with a single implementation of request/response. Stack PHP is very specific to Symfony. In the world of PSR-7, a middleware _will not know_ what the correct implementation is. A middleware would depend on the interfaces _only_ and not a specific vendor package.

> I don't understand why you are arguing so hard against something that most adopters of PSR-7 have already agreed upon informally.

All that, plus the fact you can write an adapter in like 5 minutes.

Yeah, I don't get why this is a big deal, at all.
> https://groups.google.com/d/msgid/php-fig/CAGOJM6JyEzxVSOeM%2B%3DJ8QXnxPk7TAPVCy2ytqE9VEas-oL0vDQ%40mail.gmail.com.

Fabien Potencier

unread,
May 18, 2016, 10:03:57 AM5/18/16
to php...@googlegroups.com
Just a fact, the Symfony world is not so small compared to the PSR-7
world, at least in the Fig group :) From the current member list:

Concrete5, Drupal, eZ Publish, Laravel, phpBB, PPI Framework,
PrestaShop, Sculpin, Zikula, and Symfony are using the same
HttpKernelInterface defined more than 5 years ago (I've listed the
project based on what they declare as dependencies in their composer.json).

That's 10 projects out of 40.

Not saying this is the only possibility, but I thought the Fig group was
all about trying to standardize what project members were already doing.
So, taking HttpKernelInterface from Symfony into account in the
discussion seems legitimate to me.

Don't forget to think about re-entrance, ESI support, sub-requests and HMVC.

Just my 2 cents,
Fabien
> <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/1ad1f451-fc4d-4ee0-94eb-82ae09be0444%40googlegroups.com
> <https://groups.google.com/d/msgid/php-fig/1ad1f451-fc4d-4ee0-94eb-82ae09be0444%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.
>
> --
>
> Woody Gilk
> http://about.me/shadowhand
>
> --
> 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/CAGOJM6JyEzxVSOeM%2B%3DJ8QXnxPk7TAPVCy2ytqE9VEas-oL0vDQ%40mail.gmail.com
> <https://groups.google.com/d/msgid/php-fig/CAGOJM6JyEzxVSOeM%2B%3DJ8QXnxPk7TAPVCy2ytqE9VEas-oL0vDQ%40mail.gmail.com?utm_medium=email&utm_source=footer>.

Jason Coward

unread,
May 18, 2016, 10:16:20 AM5/18/16
to php...@googlegroups.com
Fabien:

The arguments here are all boiling down to what appear to be nothing more than metrics for a popularity contest. Nothing in this PSR prevents the use of the more capable middleware style with a simple wrapper for the less capable style that is used in projects that did not consider interoperability in this area 5 years ago. The discussion here is about interoperability and providing interfaces that are the most capable. It should *not* be about reinforcing whatever someone has done for the sake of not disrupting the status quo. I am not just saying this because the project I represent happens to implement the proposed interface either. My point is that it is *very* important, at least IMO, that this standards group does not devolve into little more than a popularity contest.

And a reminder, this proposal is about HTTP Middleware based around the HTTP messaging standards established in PSR-7, not Symfony versus PSR-7 or Laravel versus PSR-7.

Cheers,

Jason Coward | Slim Framework

Woody Gilk

unread,
May 18, 2016, 10:22:48 AM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 9:03 AM, Fabien Potencier
<fabien.p...@gmail.com> wrote:
> Just a fact, the Symfony world is not so small compared to the PSR-7 world,
> at least in the Fig group :) From the current member list:

I am not trying to say that Symfony is not important. My point is that
the current proposal is targeted at PSR-7, not HttpKernel. And in
fact, as you know, Symfony already has a PSR-7 bridge:
http://symfony.com/doc/current/cookbook/psr7.html

As others have stated, it is trivial to wrap the currently proposed
interface for usage with middleware that does not expect a response to
be passed. The opposite is not true.

Jelmer Schreuder

unread,
May 18, 2016, 10:41:45 AM5/18/16
to PHP Framework Interoperability Group
There's popularity arguments being made on both sides. Both the PSR and the discussions up till now base this choice on "informal standards" and that projects are already using the pipeline-approach. Fabien came in to note that it's not exactly true, as many projects using HttpFoundation were using the decorator-approach. You can try and draw a distinction, but both PSR-7 and the HttpFoundation solve the same basic problem. So these are comparable circumstances and solutions.

Personally I am kind of partial to the decorator-approach, but I'm on the fence. Having used both, the decorator feels like a much more solid application flow to me. But lets debate the merits of both, as both have their respective uses proved. It's nonsense to discount the StackPHP/Symfony experience just because it predates PSR-7. There's valid arguments that have been made about having your library dependent upon a specific PSR-7 implementation, though that's a solvable problem in my opinion (expect a factory, or accept that you don't care if your projects uses multiple PSR-7 implementations).

The meta-document would also benefit from a bit of fleshing out on this point, beyond its popularity or perceived "informal standard" status. My suggestions would be to compare the patterns and explain why the pipeline is the preferred choice.

Woody Gilk

unread,
May 18, 2016, 10:52:39 AM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 9:41 AM, Jelmer Schreuder
<j.sch...@mijnpraktijk.com> wrote:
> But lets debate the merits of both

Please, let's not. If someone wants to propose another PSR that deals
with the decorator approach, by all means do so. But please stop
hijacking this proposal with arguments that benefit no one.

Regards,

Paul Jones

unread,
May 18, 2016, 11:18:18 AM5/18/16
to php...@googlegroups.com

> On May 18, 2016, at 09:03, Fabien Potencier <fabien.p...@gmail.com> wrote:
>
> Just a fact, the Symfony world is not so small compared to the PSR-7 world, at least in the Fig group :) From the current member list:
>
> Concrete5, Drupal, eZ Publish, Laravel, phpBB, PPI Framework, PrestaShop, Sculpin, Zikula, and Symfony are using the same HttpKernelInterface defined more than 5 years ago (I've listed the project based on what they declare as dependencies in their composer.json).

How many of them are using PSR-7, against which this interface is typehinted?

Fabien Potencier

unread,
May 18, 2016, 11:20:05 AM5/18/16
to php...@googlegroups.com
Not sure I understand your question. HttpKernelInterface does not use
PSR-7 in any way.

Fabien


Paul Jones

unread,
May 18, 2016, 11:22:38 AM5/18/16
to php...@googlegroups.com
(/me nods)

That's just my point -- this is a PSR-7-related proposal. Counting projects *might* be valid, but in this case, they'd have to be projects that are using PSR-7.

Fabien Potencier

unread,
May 18, 2016, 11:25:34 AM5/18/16
to php...@googlegroups.com
Wow, I thought you were the one who's always saying that PSRs should
take into account existing projects.

Let me ask another question: how many projects are using PSR-7 in the
current list of members?

Anyway, I don't really care anymore.

Cheers,
Fabien

Korvin Szanto

unread,
May 18, 2016, 11:27:48 AM5/18/16
to php...@googlegroups.com
Sorry Fabien, I'd agree with Paul. 
Since c5 was brought up, I should clarify that we are only using the symfony's httpkernel because we cannot break bc. We would use the http message bridge, but it does not work properly in all cases that we'd need it to work in. This is why I cited my testing middleware component instead of bringing up examples from c5. https://github.com/Buttress/Atlas/blob/master/Http/Middleware/MiddlewareMap.php

Thanks,
Korvin



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

Jordi Boggiano

unread,
May 18, 2016, 11:28:00 AM5/18/16
to php...@googlegroups.com
On 18/05/2016 14:47, Woody Gilk wrote:
> Taylor, your entire argument is couched in terms that only make sense
> with a single implementation of request/response. Stack PHP is very
> specific to Symfony. In the world of PSR-7, a middleware _will not know_
> what the correct implementation is. A middleware would depend on the
> interfaces _only_ and not a specific vendor package.

What's wrong with a middleware acting like this? Let's take the session
example which needs to set something on the response..

function (
RequestInterface $request,
callable $next
): ResponseInterface {
if ($request has no session cookie) {
$needsCookie = true;
}

initSession() // puts stuff in the request so the session is
available or whatever below

$response = $next();

if ($needsCookie) {
$response->withHeader('set-cookie', '....');
}
}

This way the response is not inject, and the response does not depend on
a concrete implementation, it just follows the PSR-7 interfaces.

So if you are wrapping symfony or laravel for example, they get a PSR-7
request, that gets converted to a HttpKernelInterface style request,
then they return a response, which gets converted to PSR-7 style
response on the way out by the symfony PSR-7 "middleware", and then
other actual PSR-7 middlewares can act on the response before it's sent out.

Cheers

--
Jordi Boggiano
@seldaek - http://seld.be

Paul Jones

unread,
May 18, 2016, 11:28:43 AM5/18/16
to php...@googlegroups.com

> On May 18, 2016, at 10:25, Fabien Potencier <fabien.p...@gmail.com> wrote:
>
> On 5/18/16 17:22, Paul Jones wrote:
>>
>>> On May 18, 2016, at 10:19, Fabien Potencier <fabien.p...@gmail.com> wrote:
>>>
>>> On 5/18/16 17:18, Paul Jones wrote:
>>>>
>>>>> On May 18, 2016, at 09:03, Fabien Potencier <fabien.p...@gmail.com> wrote:
>>>>>
>>>>> Just a fact, the Symfony world is not so small compared to the PSR-7 world, at least in the Fig group :) From the current member list:
>>>>>
>>>>> Concrete5, Drupal, eZ Publish, Laravel, phpBB, PPI Framework, PrestaShop, Sculpin, Zikula, and Symfony are using the same HttpKernelInterface defined more than 5 years ago (I've listed the project based on what they declare as dependencies in their composer.json).
>>>>
>>>> How many of them are using PSR-7, against which this interface is typehinted?
>>>>
>>>
>>> Not sure I understand your question. HttpKernelInterface does not use PSR-7 in any way.
>>
>> (/me nods)
>>
>> That's just my point -- this is a PSR-7-related proposal. Counting projects *might* be valid, but in this case, they'd have to be projects that are using PSR-7.
>>
>
> Wow, I thought you were the one who's always saying that PSRs should take into account existing projects.

I am that one. :-) Since this proposal refers to PSR-7, it seems appropriate that it should take into account existing projects that use PSR-7.


> Let me ask another question: how many projects are using PSR-7 in the current list of members?

Slim and Relay (represented by me, along with Aura et al), at the very least. Perhaps there are others?

Woody Gilk

unread,
May 18, 2016, 12:12:21 PM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 10:28 AM, Paul Jones <pmjo...@gmail.com> wrote:
>> Let me ask another question: how many projects are using PSR-7 in the current list of members?
>
> Slim and Relay (represented by me, along with Aura et al), at the very least. Perhaps there are others?

Zend Framework (in Diactoros and Stratigility) and Guzzle, too.

Jonathan Eskew

unread,
May 18, 2016, 12:21:10 PM5/18/16
to PHP Framework Interoperability Group
Guzzle's middleware contract does not expect a response to be passed in but instead requires that a response be returned (i.e., it looks a lot like what Jordi posted). Middleware should be able to handle *any* PSR-7 response returned from the next middleware in the chain, even if it's an anonymous class defined inline. The current proposal seems to be very concerned with carrying a specific implementation of ResponseInterface from middleware to middleware, which will encourage the proliferation of non-standard properties and methods IMO.

-Jonathan

Woody Gilk

unread,
May 18, 2016, 12:25:09 PM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 11:21 AM, Jonathan Eskew <jona...@jeskew.net> wrote:
> which will encourage the proliferation of non-standard properties and
> methods IMO


This makes no sense, because the type hint is always
ResponseInterface. Relying on non-interface methods would fail any
kind of mock testing.

Marco Perone

unread,
May 18, 2016, 12:33:00 PM5/18/16
to php...@googlegroups.com
From my point of view, it all comes down to what the middleware needs to know.

If a request is not passed as an argument, and the middleware has to return one, it needs to construct it. This means the the middleware itself will need to have knowledge of a particular PSR-7 implementation.

If a request is injected into the middleware (as an argument, as a constructor parameter, with a factory, ...), then the middleware doesn't need to have knowledge of any particular implementation.

There are no things that in one case can be done and not in the other. From my point of view, it is just more elegant to be very explict on what the middleware needs to know in its interface.

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

Jordi Boggiano

unread,
May 18, 2016, 12:52:04 PM5/18/16
to php...@googlegroups.com
On 18/05/2016 17:32, Marco Perone wrote:
> From my point of view, it all comes down to what the middleware needs to
> know.
>
> If a request is not passed as an argument, and the middleware has to
> return one, it needs to construct it. This means the the middleware
> itself will need to have knowledge of a particular PSR-7 implementation.

That is incorrect, please refer to my other answer in this thread for a
code example. If the $next callback returns a response, then the
middleware can return that response, and so on.

Middlewares are a typical onion-peel "pattern". You wrap one into
another and another and another, but at the very center of it all, you
have the application. If the application returns the response, it's in
charge of which PSR-7 implementation it uses, and then middlewares just
forward the response all the way out, much like they forwarded the
request all the way in.

Woody Gilk

unread,
May 18, 2016, 12:55:39 PM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 11:52 AM, Jordi Boggiano <j.bog...@seld.be> wrote:
> If the application returns the response, it's in charge of which PSR-7
> implementation it uses

The application should not create the response. The application should
not care what implementation of PSR-7 is used. The choice of
implementation belongs completely outside of the application, in the
front matter.

Jonathan Eskew

unread,
May 18, 2016, 12:57:12 PM5/18/16
to PHP Framework Interoperability Group
I would say that a middleware that could short-circuit deferring to the next middleware (e.g., a caching middleware as described earlier in this thread) does need to know how to create a response. A particular middleware that needs to do so but does not want a dependency on a particular implementation could take a ResponseInterface as a constructor argument.

Larry Garfield

unread,
May 18, 2016, 12:58:08 PM5/18/16
to php...@googlegroups.com
On 05/18/2016 05:28 PM, Paul Jones wrote:
>> On May 18, 2016, at 10:25, Fabien Potencier <fabien.p...@gmail.com> wrote:
>>
>> On 5/18/16 17:22, Paul Jones wrote:
>>>> On May 18, 2016, at 10:19, Fabien Potencier <fabien.p...@gmail.com> wrote:
>>>>
>>>> On 5/18/16 17:18, Paul Jones wrote:
>>>>>> On May 18, 2016, at 09:03, Fabien Potencier <fabien.p...@gmail.com> wrote:
>>>>>>
>>>>>> Just a fact, the Symfony world is not so small compared to the PSR-7 world, at least in the Fig group :) From the current member list:
>>>>>>
>>>>>> Concrete5, Drupal, eZ Publish, Laravel, phpBB, PPI Framework, PrestaShop, Sculpin, Zikula, and Symfony are using the same HttpKernelInterface defined more than 5 years ago (I've listed the project based on what they declare as dependencies in their composer.json).
>>>>> How many of them are using PSR-7, against which this interface is typehinted?
>>>>>
>>>> Not sure I understand your question. HttpKernelInterface does not use PSR-7 in any way.
>>> (/me nods)
>>>
>>> That's just my point -- this is a PSR-7-related proposal. Counting projects *might* be valid, but in this case, they'd have to be projects that are using PSR-7.
>>>
>> Wow, I thought you were the one who's always saying that PSRs should take into account existing projects.
> I am that one. :-) Since this proposal refers to PSR-7, it seems appropriate that it should take into account existing projects that use PSR-7.

Sure. No one is suggesting that such projects be ignored. But to
suggest that the most successful and widely used middleware architecture
in PHP right now be utterly ignored just because it pre-dated PSR-7 is
incredibly hypocritical.

We should ignore the interface used by *one in four current member
projects* just because it's more than a year old? That's beyond
hypocritical, and is the exact sort of "new cool chasing" that you,
Paul, are usually complaining about.

There's nothing in PSR-7 that mandates the use of a double-pass
middleware design. It's entirely mute on that point, by design. PSR-7
is not an equivalent or alternative to HttpKernel at all. It's an
equivalent to HttpFoundation. It's completely and entirely possible to
write a Symfony/HttpKernel/labda-style middleware design that uses PSR-7
instead of HttpFoundation. I know because I did so as part of the PSR-7
vetting process:

https://github.com/Crell/Stacker

Does that mean we must take that approach rather than double-pass? No.
It means both approaches have something going for them, both have pros
and cons, and it both should be given due good-faith consideration to
determine what the balance point is, and if there's some way to handle
both approaches. Or maybe there's a 3rd approach that has a much better
set of pros/cons than either of those.

All I (and I presume Fabien) are asking for is that a middleware working
group not jump to a conclusion that double-pass is the only meaningful
middleware design, and give others, including the most widely used de
facto standard in PHP today, a good faith evaluation.

From Woody's comments earlier in this thread it sounds like he's
unwilling to do so; if that's the case, I will be switching Drupal's
vote to No.

--Larry Garfield

Woody Gilk

unread,
May 18, 2016, 1:00:57 PM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 11:58 AM, Larry Garfield <la...@garfieldtech.com> wrote:
> From Woody's comments earlier in this thread it sounds like he's unwilling
> to do so; if that's the case, I will be switching Drupal's vote to No.


I'm in favor of a separate PSR that defines a HttpKernel style
approach, if other projects think it is of value. It is not of value
to me I think trying to combine the two proposals is a distraction.

Larry Garfield

unread,
May 18, 2016, 1:09:01 PM5/18/16
to php...@googlegroups.com
IF two separate interfaces are the best approach, I would still rather
see them developed in concert to be as compatible as possible. If, for
instance, one can be a strict superset of the other, that would be
better for all involved. (Could it be? I don't know. I want to see a
good faith evaluation of what that could look like before it's dismissed.)

--Larry Garfield

Woody Gilk

unread,
May 18, 2016, 1:11:47 PM5/18/16
to PHP Framework Interoperability Group
Interfaces cannot change method signatures by extension, so no, one
could not be a superset of the other.
> --
> 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/573CA1A7.8050203%40garfieldtech.com.

Larry Garfield

unread,
May 18, 2016, 1:17:50 PM5/18/16
to php...@googlegroups.com
On 05/18/2016 07:11 PM, Woody Gilk wrote:
> Interfaces cannot change method signatures by extension, so no, one
> could not be a superset of the other.
> --
> Woody Gilk
> http://about.me/shadowhand

1) Please don't top-post in a thread that's been bottom-posting. :-)

2) I didn't say inheritance. "Superset" just means "can we solve both
use cases with a unified model". Maybe? I don't know. That's an
investigation I'd want to see, not have dismissed.

--Larry Garfield

Marco Perone

unread,
May 18, 2016, 1:18:58 PM5/18/16
to php...@googlegroups.com
What should be avoided IMHO is that middleware create a response using `new`.
Hence, if it needs to be able to short-circuit, it needs to receive the response somehow. I think there are three viable options:

1) pass a ResponseInterface as a method parameter
2) pass a ResponseInterface as a constructor argument
3) pass a ResponseInterface factory as a constructor argument

From my point of view, the benefit of 1) is that it could be easly part of the MiddlewareInterface, making everything very explicit, while doing that with the other two would look a bit more clumsy.



Paul Jones

unread,
May 18, 2016, 1:35:58 PM5/18/16
to php...@googlegroups.com
Hi Larry,

On May 18, 2016, at 11:58, Larry Garfield <la...@garfieldtech.com> wrote:

> No one is suggesting that such projects be ignored. But to suggest that the most successful and widely used middleware architecture in PHP right now be utterly ignored just because it pre-dated PSR-7 is incredibly hypocritical.

/me furrows brow

I'm not suggesting project be ignored because they predates PSR-7, per se. I'm suggesting that those projects *don't use* PSR-7. This proposal refers directly to PSR-7, so it seems reasonable to talk about PSR-7 projects in relation to it.

If, during the "draft" phase, group members want to make more extended arguments about changing the proposal based on non-PSR-7 projects (including maybe even non-PHP projects!), I'm sure the various ideas will be entertained.

Oscar Otero

unread,
May 18, 2016, 1:43:07 PM5/18/16
to php...@googlegroups.com
IMO, the ($request, $response, $next) signature has some advantages vs ($request, $next)

1) Passing the response instance or response factory as a constructor argument does not allow to use closures or other callables as middleware components.

2) There’s some use cases in which the response is useful to store values before the app. An example is negotiation components (content-type, content-language, etc). Let’s say we have the following contentTypeNegotiator:

function contentTypeNegotiator ($request, $response, $next) {
    $contentType = getContentType($request);
    $response = $response->withHeader('Content-Type', $contentType);

    return $next($request, $response);
}

The inner components can get the content-type of the response, for example, to display error messages:

function errorResponse ($request, $response, $next) {
    $response = $next($request, $response);

    $contentType = $response->getHeaderLine('Content-Type');
    $body = $response->getBody();

    switch ($contentType) {
        case 'text/plain':
        case 'text/css':
        case 'text/javascript':
            $body->write(sprintf('Error %s', $response->getStatusCode()));
            break;

        case 'application/json':
            $body->write(json_encode(['error' => $response->getStatusCode()]);
            break;

        default:
            $body->write(sprintf('<html><h1>Error %s</h1></html>', $response->getStatusCode()));
            break;
    }
}

The output format could be saved as an attribute, but this way is more portable.


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.

Jonathan Eskew

unread,
May 18, 2016, 1:58:28 PM5/18/16
to php...@googlegroups.com

Middleware that modifies a response could invoke `$next` and apply changes to the response returned. Closures can close over a response or response factory. These are both things that Guzzle's PSR-7 compliant middleware does frequently without requiring that an empty response be passed from middleware to middleware.

In other languages, the `(request, response, next): void` signature is used when a response is a vehicle for side effects (such as writing to a ResponseWriter in Go or to res.send in Express.js). You might also opt to use that signature if the response is a mutable "out parameter." That's not the case with PSR-7, where responses are immutable value objects that adhere to a well-defined interface.

Woody Gilk

unread,
May 18, 2016, 2:01:46 PM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 12:58 PM, Jonathan Eskew <jona...@jeskew.net> wrote:
> Middleware that modifies a response could invoke `$next` and apply changes
> to the response returned.


Not if they want to abort the request and stop processing further middleware.

Larry Garfield

unread,
May 18, 2016, 2:06:31 PM5/18/16
to php...@googlegroups.com
You are assuming that double-pass "is" a PSR-7 middleware, whereas
lambda-style is not. If the topic on the table is a middleware standard
built on PSR-7, shouldn't the full scope of existing middleware designs
(including those that use not PSR-7 but something similar) be considered?

--Larry Garfield

Paul Jones

unread,
May 18, 2016, 2:10:32 PM5/18/16
to php...@googlegroups.com

> On May 18, 2016, at 13:06, Larry Garfield <la...@garfieldtech.com> wrote:
>
> If the topic on the table is a middleware standard built on PSR-7, shouldn't the full scope of existing middleware designs (including those that use not PSR-7 but something similar) be considered?

Maybe, maybe not. I'm just trying to (kindly) correct your statements about my statements. ;-)

Oscar Otero

unread,
May 18, 2016, 2:12:42 PM5/18/16
to php...@googlegroups.com
If you want to use a firewall, csrf protection, authentication, etc, they only need to abort the process and return a $response->withStatus(403) without worring about the format of the response message.
> --
> 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/CAGOJM6J_TLpDUG6LvaG7bzbNprC3b74eNaOh6ZSmed33eg72xQ%40mail.gmail.com.

Jonathan Eskew

unread,
May 18, 2016, 2:19:58 PM5/18/16
to PHP Framework Interoperability Group
In that case, you could simply return a response that the middleware creates (with `new`, by pulling one from a factory, by calling a `with*` method on a response that had been closed over or passed in via constructor). 

Korvin Szanto

unread,
May 18, 2016, 2:24:17 PM5/18/16
to php...@googlegroups.com
On Wed, May 18, 2016 at 11:20 AM Jonathan Eskew <jona...@jeskew.net> wrote:
In that case, you could simply return a response that the middleware creates (with `new`, by pulling one from a factory, by calling a `with*` method on a response that had been closed over or passed in via constructor). 

The point that constructors aren't available to closures still stands. The only way one could achieve this is by accepting a factory with a use statement. To me it sounds like any package that wants to only contain middleware must also have some base response classes as well. This combined with the overwhelming support for both request and response to be passed in was why I switched my buttress implementation from ($request, $next) to ($request, $response, $next).
 
 
On Wednesday, May 18, 2016 at 11:01:46 AM UTC-7, Woody Gilk wrote:
On Wed, May 18, 2016 at 12:58 PM, Jonathan Eskew <jona...@jeskew.net> wrote:
> Middleware that modifies a response could invoke `$next` and apply changes
> to the response returned.


Not if they want to abort the request and stop processing further middleware.
--
Woody Gilk
http://about.me/shadowhand

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

Woody Gilk

unread,
May 18, 2016, 2:29:18 PM5/18/16
to PHP Framework Interoperability Group
On Wed, May 18, 2016 at 1:19 PM, Jonathan Eskew <jona...@jeskew.net> wrote:
> In that case, you could simply return a response that the middleware creates
> (with `new`, by pulling one from a factory, by calling a `with*` method on a
> response that had been closed over or passed in via constructor).


And what benefit does that have over the currently proposed signature?
In my estimation: none. And you can't create an interface that
enforces it in any reasonable way. And you can't decorate the response
before it reaches that middleware, so every possible change previously
made will be silently discarded.

Sounds like a nightmare to debug and much more difficult to document.

Korvin Szanto

unread,
May 18, 2016, 2:31:07 PM5/18/16
to php...@googlegroups.com
My apologies for the double post:

On Wed, May 18, 2016 at 2:15 AM Larry Garfield <la...@garfieldtech.com> wrote:
An important consideration is that while double-pass style (passing a
request and a response all the way through the pipeline) may be the most
popular approach by number of distinct implementations right now, by
number of installations using it the lambda-style (request in, response
out) is far, FAR more widely used.  Although Fabien has openly said he
doesn't like the middleware approach, the Symfony HttpKernelInterface is
now at the heart of dozens of projects (including several members
here)[1].  If we're looking to standardize middlewares we should not
ignore that, prior to the last 12 months, HttpKernelInterface was the de
facto PHP middleware architecture. 

[1] http://symfony.com/components/HttpKernel

--Larry Garfield


It sounds like you're pitting the two against eachother when that isn't really how it is. If you look at the existing proposal and most if not all implementations that follow a similar pattern, it's not as if double-pass disallows lambda-style at all. The only difference between the two is that a response is passed into one and not the other, both still return ResponseInterface's.

To me it boils down to the fact that I can do everything lambda-style can do with double-pass style, but the opposite doesn't ring true.

One argument that I've heard from most people I've shown my middleware stack to is that ($request, $response, $next) is quite a bit more confusing to developers not familiar with the pattern than just ($request, $next).

Thanks,
Korvin
 
On 05/11/2016 10:40 PM, Woody Gilk wrote:
> There is actually a simple way to remove all headers:
>
>
> $headers = array_keys($response->getHeaders());
>
> foreach ($headers as $header) {
>      $response = $response->withoutHeader($header);
> }
>
>
> This might not be the most elegant thing ever, but it is simple
> enough. There are also other user-land ways to create an entirely
> clean Response object. Basically, I don't think there is a strong
> enough argument or enough real-world examples to make this a valid
> concern.
>
> As per your example of an error being cached, normally a Cache-Control
> header would be set on output as part of a middleware. By this point,
> the entire request would have successfully executed and the only place
> an exception could be occurring would be in middleware itself. While
> this may be a remote possibility, the likelihood in a production
> application seems very remote to me.
>
> There is also nothing in the current proposal that prevents having a
> RequestFactory or ResponseFactory injected into middleware that needs
> it. If there was such a need, it could be done as:
>
>
> if ($this->whateverCondition($request, $response)) {
>      $response = $this->responseFactory->make();
> }
>
> // ... keep calm and carry on
>
>
> Again, I think the current proposal does not need to address this
> scenario because it is an edge case that can be addressed as necessary
> in specific implementations.

>
> --
> Woody Gilk
> http://about.me/shadowhand
>
>
> On Wed, May 11, 2016 at 3:28 PM, Ross Tuck <m...@rosstuck.com> wrote:
>> Apologies for the novel below.
>>
>>
>> @Woody Putting the off-the-cuff Etag example aside, the body is less of a
>> problem than the headers. You can easily replace the body of a response but
>> PSR-7 has no good way to replace all headers, short of deliberately omitting
>> all of the ones you can think of.
>>
>>
>> Imagine this example instead: You're building a middleware that catches
>> exceptions and renders an error page when one occurs. When there's no
>> exceptions, you just return the received response. Easy, works great! :)
>>
>>
>> Except one day you're debugging a page, the request goes all the way to the
>> app, some header gets set in the controller but then something happens and
>> an exception is thrown. The error middleware catches it, changes the body to
>> the error page and then returns the response. And then suddenly the error
>> won't go away because the Cache-Control header the app set wasn't cleared by
>> the Error middleware. Or it tries to download the error page because the
>> Content-Disposition was set. Or it works fine locally but not in production
>> because it remembered to remove max-age but not s-maxage.
>>
>>
>> There's all sorts of stuff like this that could be avoided by either a)
>> resetting all headers or b) returning a new instance. As mentioned, PSR-7
>> doesn't have a good way to void all headers, it can only remove a
>> specifically mentioned header, one at a time. Short of writing a
>> white-list-copy method, it seems much easier to just return a new blank
>> instance.
>>
>>
>> So, this isn't an issue that effects all (or even most) middleware but it
>> really makes life hard for those that it does hit.
>>
>>
>> Still, there's resistance to allowing middleware to create new instances,
>> mainly because (and I'm interpreting) folks are worried about middlewares
>> including bloat via alternate PSR-7 implementations or having conflicting
>> version requirements. As it stands, there are three proposals to fix this:
>>
>>
>> 1) We just allow them to include these alternate implementations. It's all
>> the same interface and there's only a handful of implementations on the
>> market.
>>
>> 2) Michael Dowling suggests a ResponseFactory that can be injected via
>> constructor. This is a really good technical solution (though I'd recommend
>> it include Request as well). My concern is that it will make the PSR seem
>> more complex and we won't be able to teach folks to use it properly and
>> they'll return new instances anyways.
>>
>> 3) Anthony Ferrera suggests a compromise: inject a blank/default response
>> (or request) into the constructor. This is pretty simple, doesn't require
>> any extra interfaces, plays well with their immutable nature and still lets
>> folks configure everything by just using their DI containers. And it only
>> effects those special middleware that need this. :)
>>
>>
>> Personally, I think #3 is a winner, both technically and in terms of a
>> compromise that people can accept/teach. The constructor interfaces on the
>> middleware will enforce good practice.
>>
>>
>> This might seem to render it a non-issue but there's still one underlying
>> issue: We can't make anyone do this with their custom middleware. It is
>> _really_ important that we recommend treating all PSR-7 implementations as
>> equal and the dispatchers shouldn't count on getting the same concrete type
>> back. It's just not enforceable with PHP's type system. If a dispatcher
>> really needs its custom type, we should recommend they convert it back,
>> never assume.
>>
>>
>> I can take or leave removing the Response parameter, that's not a huge deal
>> to me, but treating the implementations as interchangable is really
>> important.
>>
>>
>> One last thing and then I'll shut up. :) With regards to the client/server
>> middleware and the trait accompanying it, I haven't seen much client
>> middleware out there and I wonder if trying to capture that will make this
>> PSR more complex (when it should be a really simple slam dunk).
>>
>>
>> What would folks think about moving Client middleware to a separate PSR and
>> keep the scope here on just server middlewares, which have a clear, pressing
>> need?
>>
>>
>> That would also seem to be a +1 for the named method argument, since we
>> could release a method that typehints on ServerRequestInterface, then later
>> release another PSR for client middleware. If your middleware supports both,
>> cool, just implement two interfaces :) (There's a caveat here with the
>> ServerRequest extending Request, but I have a couple ideas about that)
>>
>>
>> On Wednesday, May 11, 2016 at 5:43:21 PM UTC+2, Korvin Szanto wrote:
>>> I worked on my own middleware enabled psr-7 request handler for awhile and
>>> I went through all these oscillations we are talking about here. I'm typing
>>> this up on a phone so bear with me.
>>>
>>> Firstly I ran into the attribute issue and asked around and it sounded
>>> like it was just an oversight. Another issue I ran into was the fact that
>>> there is no way to see if an uploaded file has been moved previously. We
>>> throw an ambiguous exception when the file has been previously moved and
>>> it's as far as I am aware impossible to properly handle the error case.
>>> Perhaps this is a good argument for a new psr-7 updated replacement.
>>>
>>> On __invoke vs a properly named handler method, the reason I opted for a
>>> named handler method in the beginning is because it felt more natural to
>>> have my dispatcher typehint on my middleware interface rather than hinting
>>> on callable. I eventually conceded to using __invoke and hinting on callable
>>> due to pressure from other implementations. Because of this, I just opted to
>>> hint on callable and use closures instead of a real interface implementation
>>> [1].
>>> See my dispatcher [2] which uses my overly complex pipeline [3] to send
>>> the request and response through a stack of middleware.
>>>
>>> I recognize that it's more flexible but it really does feel wrong to me
>>> for reasons I cannot articulate. I honestly would much rather we have a
>>> named method for handling instead of using the magic method.
>>>
>>> I 100% agree that both request and response need to be passed into the
>>> handler method. Passing in both allows you to choose whether you will return
>>> a new instance or will modify the running instance. Simply using a factory
>>> method or creating a new response every time just doesn't do it in my
>>> opinion. Though those options are still available if the response is passed
>>> in.
>>>
>>> [1]
>>> https://github.com/Buttress/framework/blob/master/test/Test/Http/RequestHandlerTest.php
>>> [2] https://github.com/Buttress/Http/blob/master/RequestHandler.php
>>> [3] https://github.com/Buttress/Pipeline/blob/master/Pipeline.php
>>>
>>> Thank you everyone for the discussion, it's a much needed distraction.
>>> Korvin
>>>
>>>
>>>
>>> On Tue, May 10, 2016 at 2:03 PM Marco Perone <pasa...@gmail.com> wrote:
>>>> Consider the following case, using the signature you are suggesting
>>>>
>>>> public function __invoke(Request $request, callable $next)
>>>> {
>>>>      if ($this->goOn()) {
>>>>          return $next($request);
>>>>      }
>>>>
>>>>      // return a default Response
>>>>      return new MyResponseImplementation();
>>>> }
>>>>
>>>> If I don't have a $request passed in as a method argument, I must depend
>>>> on a concrete implementation of the ResponseInterface.
>>>>
>>>> On the other hand, if a $response is passed in as an argument, I have a
>>>> prototypical Response I can modify, not relying on any concrete
>>>> implementation.
>>>>
>>>>
>>>> On Tuesday, May 10, 2016 at 10:19:14 PM UTC+2, Ross Tuck wrote:
>>>>> With respect, I'm not sure Dependency Inversion is a complete reason to
>>>>> inject the response here.
>>>>>
>>>>> Even if we do inject the Response, they're essentially value objects in
>>>>> the HTTP scope and it would be exceedingly common for someone to just return
>>>>> a different instance, rather than modify the instance passed in. I'd go so
>>>>> far as to call this a feature for many use cases: if I'm writing an ETag
>>>>> middleware and want to return a cache hit, I don't want to manually truncate
>>>>> the body and clear other headers off when it would be easier to just return
>>>>> a new response and to my knowledge, PSR-7 doesn't have a
>>>>> "createBlankInstance" factory method.
>>>>>
>>>>> Considering how easy (and occasionally reasonable) it is to return a new
>>>>> instance within any given middleware, I don't think including a response
>>>>> provides _any_ guarantee about the return value and we should bake that
>>>>> assumption into the spec recommendations from the outset, lest we end up
>>>>> with a bunch of soft dependencies or LSP violatons. if a
>>>>> dispatcher/framework needs a particular type of object internally, it would
>>>>> be better to convert to/from PSR-7 objects before/after the middleware
>>>>> dispatcher cycle, rather than assume it's going to get the same type at the
>>>>> end. "My\Special\Response::fromPsr7Response($response);"
>>>>>
>>>>> Just my 2 cents, tapping out for the night. :)
>>>>>
>>>>> On Tuesday, May 10, 2016 at 9:46:46 PM UTC+2, Woody Gilk wrote:
>>>>>> On Tue, May 10, 2016 at 2:36 PM, Ross Tuck <m...@rosstuck.com> wrote:
>>>>>>> why do we have the Response added in as a parameter? Why can't it be
>>>>>>> solely
>>>>>>> a return value?
>>>>>> I can answer this very simply: Dependency inversion does not allow it.
>>>>>>
>>>>>> If your middleware is only ever type hinted against PSR-7
>>>>>> request/response interfaces (as it should be) then by what mechanism
>>>>>> do you create a response? How do you know what vendor is going to
>>>>>> provide the expected implementation? How do you instantiate the
>>>>>> response class correctly?
>>>>>>
>>>>>> The short answer is that you don't and you shouldn't. The correct
>>>>>> thing to do is invert the dependencies and create the request and
>>>>>> response objects early on in application execution and pass them
>>>>>> through the stack:
>>>>>>
>>>>>> dispatch(request, response) : response
>>>>>>
>>>>>> In this way, you can be reasonably sure that the final response is
>>>>>> going to be the same implementation (but most likely not the same
>>>>>> instance, due to immutability) as the response that was initialized
>>>>>> early on.

>>>>>>
>>>>>> --
>>>>>> Woody Gilk
>>>>>> http://about.me/shadowhand
>>>> --
>>>> 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

>>>> 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.
>> To view this discussion on the web visit

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

Rasmus Schultz

unread,
May 18, 2016, 3:29:49 PM5/18/16
to php...@googlegroups.com
Good God, this thread.

Look, guys - no one is ignoring Symfony.

This PSR is about interoperability of middleware across many
dispatchers and frameworks, yes?

The request-only format can only be supported by Symfony. It cannot be
supported by other dispatchers that follow the more capable proposed
format.

The request/response double-pass can be easily supported by Symfony
and all other dispatchers. No breaking changes, no hassle - for
anyone.

How did this thread turn into some sort of framework war?

Every framework and dispatcher is covered and accounted for.

There is no problem.

What are we even debating?
> 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.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/CANeXGWUK4Q20mEx0k_8XooqU87vD_BsubmdiMz4%2Bwju2%2BuDVcQ%40mail.gmail.com.

Marco Perone

unread,
May 19, 2016, 3:12:20 AM5/19/16
to php...@googlegroups.com
Since there were many things worth of discussion in this thread, I decided to write them down to make some order (firstly in my mind). The result is this gist: https://gist.github.com/marcosh/67462c5c04e0f190b9fc5a619c790598. I hope it could be helpful for the discussion (improvements on the document are welcome). If it is not, just forget about it...

TL;DR: Writing the gist an idea came to mind:

consider the following interfaces:

interface MiddlewareInterfaceWithResponse
{
    public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next);
}

interface MiddlewareInterfaceWithoutResponse
{
    public function __invoke(RequestInterface $request, callable $next);
}

It would be nice if we could use something like the following to pass from an interface to the other

class MiddlewareTransformer
{
    public static function removeResponse(
        callable $middleware,
        ResponseInterface $response
    ) {
        $middlewareWithoutResponse = function ($request, $next) use ($middleware, $response) {
            $nextWithResponse = function ($request, $response) use ($next) {
                return call_user_func($next, $request);
            };

            return call_user_func($middleware, $request, $response, $next);
        };

        return $middlewareWithoutResponse;
    }

    public static function addResponse(callable $middleware)
    {
        $middlewareWithResponse = function ($request, $response, $next) use ($middleware) {
            $nextWithoutResponse = function ($request) use ($response, $next) {
                return call_user_func($next, $request, $response);
            };

            return call_user_func($middleware, $request, $nextWithoutResponse);
        };

        return $middlewareWithResponse;
    }
}

I'm not sure if this actually works, but if it could I guess it is worth a try.

Ross Tuck

unread,
May 19, 2016, 3:38:41 AM5/19/16
to PHP Framework Interoperability Group
Slightly different topic: A good point was raised on Reddit about how (without depending on an implementation) a middleware can't create a new Stream and thus replace the body. The PSR-7 spec literally recommends creating a new instance when in doubt, but I don't see a way to do that in any of the tech proposals that have been discussed so far. (This is also true to a lesser degree with URIs and UploadedFiles but short of rewriting incoming interfaces I don't really see a use case offhand?) I've checked around and seen a couple existing implementations have this problem.

Even if this is limited to just Response, Streams (and maybe Request), it seems like this goes past either style and maybe we should consider a general factory, either for constructor injection, parameters or merged with the next callable.

Stephan Hochdörfer

unread,
May 19, 2016, 3:55:56 AM5/19/16
to PHP Framework Interoperability Group
Am Mittwoch, 18. Mai 2016 18:55:39 UTC+2 schrieb Woody Gilk:
The application should not create the response. The application should
not care what implementation of PSR-7 is used. The choice of
implementation belongs completely outside of the application, in the
front matter.


This is in fact for only valid reason for me to go this way even though I would really, really prefer the request in / response out way. The problem is that $next() might not need to be called (e.g. authentication fails and I want to return a response quickly). Given that the middleware would need to create its own response object which means the middleware needs to depend on a concrete PSR-7 implementation. What if I use 3 different middlewares from 3 different vendors each implementing PSR-7 in their own way? Sure all the instances implement the same interface so potentially all provide the same methods and thus they are compatible. But what if one implementation contains a bug that no one spotted so far? Debugging that issue would be quite painful I guess.

Stephan Hochdörfer 

Woody Gilk

unread,
May 19, 2016, 9:05:48 AM5/19/16
to PHP Framework Interoperability Group
On Thu, May 19, 2016 at 2:38 AM, Ross Tuck <m...@rosstuck.com> wrote:
> Slightly different topic: A good point was raised on Reddit about how
> (without depending on an implementation) a middleware can't create a new
> Stream and thus replace the body.


Please, let's not confuse the discussion about a middleware interface
further by talking about streams and URIs.
It is loading more messages.
0 new messages