PSR-7 (HTTP Message Interfaces)

790 views
Skip to first unread message

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 12:10:57 PM9/26/14
to php...@googlegroups.com
Greetings!

First, this is a long email. If you're not interested in PSR-7 (HTTP Message Interfaces), just stop reading now and move along. I have a lot of background I want to cover.

The tl;dr, however is this: after discussion with Paul M. Jones, Phil Sturgeon, and Beau Simenson, I'm offering to drive forward PSR-7 as Editor.

Also, a note: I wrote this using markdown, and hence the _wierd_ **emphasis** and `code` markers.

Around a month ago, I started work on porting Sencha's Connect (a Node library) to PHP.

One thing I find quite interesting about Node is that code you write to handle an incoming HTTP request, and code you write to populate the response, can be dropped into essentially any Node server-side HTTP framework and "just work" (tm). Why? Because Node has built-in core objects for HTTP messages, and a standard way of defining code that handles an HTTP request (`function (request, response)`). 

I quite like this.

It's very liberating. You don't think of yourself as an "Express" or a "Hapi" developer, you think of yourself as an API or Web developer. You're working with HTTP, and just that. Any other abstractions you deal with? Those are incidental. If I decide I want to use a different "engine", I can do that, and it has few if any ramifications on the code I just wrote.

The reason I wanted to port Connect is this: an application consists of middleware. Each middleware is a callback that accepts a request, response, and a callback called "next" (which is optional, actually):

    function (request, response, next)

If the callback is self-contained, it writes to the response and returns, and the request is over (this is why `next` is optional; you might never call it). If more work can be done, it calls `next()`, and the next middleware in the stack is executed. (It can optionally do more work _after_ calling `next()`, which is how you can implement things like caching, logging, etc.) It also defines error handling: passing a value to `next()` indicates an error condition, as does raising an exception; in those cases, the first middleware that accepts exactly four arguments:

    function (err, request, response, next)

is invoked. That function can pass on to the `next()` if desired, allowing for chains of specialized error handlers. (As an example, I wrote one that handled 403 statuses by presenting a login form, and chained it on to one that handles 404 statuses with a "not found" page, and chained that to a more generalized 500 handler.)

Connect is based on Ruby's Rack, but differs in the `next()` argument; Rack does something interesting in that it injects the controlling application and/or middleware into the invoked handler object, and then the handler can call on that to invoke the next middleware if desired. Connect takes a more functional paradigm (as in functional programming, not the judgment statement) and instead passes the function that does the stack iteration itself -- which lends itself to adoption in existing code much more simply (no need to define an injection method, or have awareness of the invoking application).

It also means that a single application can often mix different frameworks by segregating them on different paths. This is powerful; if a good solution exists that targets one framework, you can compose it in:

    app.use('/blog', BlogApplication);
    app.use('/api', ApiApplication);

This allows each to have their own defined workflow, completely segregated from the other, but still part of the same application -- and fully compatible because they all support the same basic paradigm: an HTTP handler accepts a request and a response object.

Simple patterns == simple implementation. Usually.

This brings me back to porting Sencha Connect to PHP; I wanted to bring this concept to PHP.

Connect is more of a reference implementation for middleware than anything else. Express was based on it (as in, had it as a dependency) through version 3, and in version 4, essentially brought the core Connect code directly into its own library as an implementation detail. Other Node frameworks also build on it, or at least are compatible with it.  It's a nice, small library, an interesting take on Rack, and as such, a nice first target to port.

The only real stumbling block was this: Node has first-class concepts of HTTP messages (requests and responses) built into its core, and PHP does not. I debated using request/response pairs from existing frameworks (ZF2 was an obvious choice for me; I also considered Symfony's HttpFoundation and Aura.Http), but I was not enthusiastic about the decision. This stuff should just be baked into PHP.

I then remembered that a proposal was in front of FIG, PSR-7. I checked, and the interfaces had already been published as a Composer package, but there were no implementations. So I wrote one:


It was surprisingly easy; I had the basic functionality within a few hours, and had it polished within a few days (thank you, scrutinizer-ci!). I've not found anything missing in the interfaces so far; they largely mimic what's available in Node, even down to the use of streams for the content. (Which seems like an odd choice until you actually use them.)

I know from Michael Dowling that the original intent for PSR-7 was to define HTTP messages that could then be used in HTTP _clients_. I am here to argue that they are even more important when considering server-side applications.

PSR-7 opens an interesting vector for framework/application interoperability unlike anything since we wrote the reference autoloader implementation that became PSR-0. I don't know about you, but I've had to volley request/response pairs between framework layers many times, and it's always painful. The decent implementations allow you to set everything; the poor ones pull everything directly from superglobals, which can lead to interesting issues particularly when you get to the request content (as `php://input`, until 5.6, was read-once). I have literally wasted days trying to cast request/response pairs between projects.

As noted earlier, using streams for the content seems like it would be awkward when you read about it in the spec -- but it's actually a quite natural fit. On the server side, for the incoming request, your request content, if you have any, is already modelled as a stream by PHP (`php://input`). For output, if you want to send a file, it's normally a PITA to retrofit a Response object to do this, or you end up working around the response object and using something like `readfile()` directly; having streams built in means I can pass `new Stream(fopen('filename'))` to the content and be done. For normal responses, I can use a PHP memory or temp stream, and not worry about concatenation (this is in fact exactly what I did for https://github.com/phly/conduit); I just write to it. It's quite flexible, and not at all burdensome (particularly if you can write a decorator or proxy that allows you to call `$response->write(...)`).

If frameworks adopt PSR-7, it means we can now effectively mix-and-match things even as framework-bound as controllers without too much difficulty -- because marshaling input from the request will be the same across implementations, and writing to the response will be the same across implementations. You just pass the current Request/Response to them.

PSR-7 also makes the possibility of middleware, ala Rack and Connect, possible at a larger scale, and points in the direction of a PHP ecosystem where middleware becomes the common denominator, not frameworks. I **very** much like that idea, and think it goes hand-in-hand with the advances Composer has made possible. Instead of choosing a framework, or having to choose a framework because a particular implementation was written for that framework, developers can write HTTP-centric code using PSR-7 interfaces and best-of-breed components gleaned from Composer - and anybody can drop _that_ code into _any_ application, regardless of framework. Instead of a ZF2 contact form module, you get contact form middleware you can drop into _any_ application; or a blog; or whatever.

Yes, others have pushed this idea in the PHP ecosystem before: http://stackphp.com. I spoke with Beau a couple weeks ago about this, and he is actually very excited about the possibility of Stack being based on a framework-agnostic request/response pair; it only furthers the goals of Stack, which is to promote middleware for the entire PHP community. As such, he and I are aligned on the direction of PSR-7.

In a nutshell: what I dream of for PHP is that we stop thinking of ourselves as Zend Framework, or Symfony, or Laravel, or Aura, or FrameworkFlavorOfTheDay developers, but instead think of ourselves as PHP, HTTP, or Web developers.

I think PSR-7 would be a huge step towards this, and towards reducing tribalism within PHP.

With Michael stepping down as the lead on PSR-7 (https://groups.google.com/forum/#!topic/php-fig/XwFcqSmqzGk), I have decided to step forward and help push PSR-7 to adoption.

This is the part where you all jump in and say, "I thought you quit the FIG?" (Michael even referenced my own blog post as rationale for stepping down!) I know, I know... but I think this is incredibly important, possibly more so even than PSR-0 originally was.

I've spoken with Paul, Phil, and Beau, and they are willing to sponsor if I act as Editor for the proposal.

So, I've said my piece.

What I want to hear from YOU is:

- What do you think is missing from PSR-7 as it currently stands, and, more importantly, why?
- Have you built anything with the interfaces as they currently stand? What were your experiences?
- Have you built anything with middleware before, in any language? If so, what feedback do you have?

I want to get PSR-7 to review stage, and a vote, as soon as possible; this has been lingering far too long as it is, particularly considering how well-formed and useful the interfaces already are!

References:

- phly/http (reference implementation of PSR-7): https://github.com/phly/http
- phly/conduit (port of Connect using phly/http): https://github.com/phly/conduit
- Michael Dowling's retraction from PSR-7: https://groups.google.com/forum/#!topic/php-fig/XwFcqSmqzGk

-- 
Matthew Weier O'Phinney

John Nickell

unread,
Sep 26, 2014, 12:30:18 PM9/26/14
to php...@googlegroups.com
Wow!

I have followed the PSR standards and this Google group for a while, but didn't feel the need to post until now.

I completely agree with your assessment of the importance of a PSR like this. Standardizing around HTTP message interfaces could pave the way for a standard middleware interface, like Symfony's HttpKernelInterface.

This group is about interoperability, right?

I can't wait to see what you have planned, and read your implementation.

Thank you Matthew.

John Nickell

Paul M. Jones

unread,
Sep 26, 2014, 12:31:44 PM9/26/14
to php...@googlegroups.com
On Sep 26, 2014, at 11:10 AM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:

> Greetings!

Hi Matthew. Nice to see you back in action here.


> The tl;dr, however is this: after discussion with Paul M. Jones, Phil Sturgeon, and Beau Simenson, I'm offering to drive forward PSR-7 as Editor.

I am of course in favor of this.


> The only real stumbling block was this: Node has first-class concepts of HTTP messages (requests and responses) built into its core, and PHP does not. I debated using request/response pairs from existing frameworks (ZF2 was an obvious choice for me; I also considered Symfony's HttpFoundation and Aura.Http), but I was not enthusiastic about the decision. This stuff should just be baked into PHP.

(For what it's worth, and for what you're describing, Aura.Web (not Aura.Http) might have been the more appropriate library, but whatever.)

It sounds like the idea is not generic HTTP messages per se, but specifically a model of the HTTP request received by PHP, and a model of the outgoing HTTP response to be delivered by PHP. (This is as opposed to a request issued by PHP to a remote API and the resulting response being received by PHP in return.)

If so, I'm in favor of the former being the proper focus of PSR-7. That in turn probably means some tweaking on the Request portion.


> As noted earlier, using streams for the content seems like it would be awkward when you read about it in the spec -- but it's actually a quite natural fit. On the server side, for the incoming request, your request content, if you have any, is already modelled as a stream by PHP (`php://input`). For output, if you want to send a file, it's normally a PITA to retrofit a Response object to do this, or you end up working around the response object and using something like `readfile()` directly; having streams built in means I can pass `new Stream(fopen('filename'))` to the content and be done. For normal responses, I can use a PHP memory or temp stream, and not worry about concatenation (this is in fact exactly what I did for https://github.com/phly/conduit); I just write to it. It's quite flexible, and not at all burdensome (particularly if you can write a decorator or proxy that allows you to call `$response->write(...)`).

The concentration on the server-side portion of request/response makes that more sensible to me.


> With Michael stepping down as the lead on PSR-7 (https://groups.google.com/forum/#!topic/php-fig/XwFcqSmqzGk), I have decided to step forward and help push PSR-7 to adoption.

Hear hear!


> I've spoken with Paul, Phil, and Beau, and they are willing to sponsor if I act as Editor for the proposal.

Seeing as there are already two sponsors (Phil and Beau) I don't think I'm actually needed; that is, unless one of them chooses to step down.


> - What do you think is missing from PSR-7 as it currently stands, and, more importantly, why?

I think the finer focus on the the Request being "what PHP receives from the client" and the Response being "what PHP sends back to the client" is hugely important. As such I think there are likely to be some light modifications for convenient access to the execution environment, things like access to $_GET/POST/FILES/COOKIE, perhaps $_ENV, and of course $_SERVER elements that are not specifically HTTP-related. Sending of cookies in the Response may become important as well.


> - Have you built anything with the interfaces as they currently stand?

No.


> - Have you built anything with middleware before, in any language? If so, what feedback do you have?

No, and no feedback other than middleware is just another way of doing things, and that the renewed focus on request/response relating specifically to the server-side interaction should not preclude other ways of doing things.


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

Modernizing Legacy Applications in PHP
http://mlaphp.com



Evert Pot

unread,
Sep 26, 2014, 12:38:46 PM9/26/14
to php...@googlegroups.com

On Friday, September 26, 2014 5:10:57 PM UTC+1, Matthew Weier O'Phinney wrote:
Greetings!

I've spoken with Paul, Phil, and Beau, and they are willing to sponsor if I act as Editor for the proposal.

+1 awesome! C
 

So, I've said my piece.

What I want to hear from YOU is:

- What do you think is missing from PSR-7 as it currently stands, and, more importantly, why?
- Have you built anything with the interfaces as they currently stand? What were your experiences?
- Have you built anything with middleware before, in any language? If so, what feedback do you have?

I implemented/borrowed from PSR-7, but only partly, here:

https://github.com/fruux/sabre-http/

I chose to ignore the stream object entirely, because for my purposes I need to have access to a stream resource.
This is my one and only issue with the spec as it stands.

1. I need to be able to call file_put_contents('php://output', $response->getBody()); // or equivalent
2. I want to be able to throw streams into stream_select(), or even libevent.
3. I want XMLWriter to write directly to a stream.
4. I want to be able to read from incoming HTTP requests and throw it in (any) parser that can accept streams as well as strings.

So if a 'Stream' object still exists, I can live with that, but for this to work efficiently for me I need some way to access the underlying stream resource is a standard manner.

PHP Stream resources for better or worse are *the* standard way to do this, and we should use this standard and not invent our own inferior version of it.

Good luck!
Evert

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 1:10:50 PM9/26/14
to php...@googlegroups.com
On Fri, Sep 26, 2014 at 11:31 AM, Paul M. Jones <pmjo...@gmail.com> wrote:
> On Sep 26, 2014, at 11:10 AM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:
> It sounds like the idea is not generic HTTP messages per se, but specifically a model of the HTTP request received by PHP, and a model of the outgoing HTTP response to be delivered by PHP. (This is as opposed to a request issued by PHP to a remote API and the resulting response being received by PHP in return.)
>
> If so, I'm in favor of the former being the proper focus of PSR-7. That in turn probably means some tweaking on the Request portion.

Yes. That said, as I noted in my original post, BOTH the client /
server use cases CAN definitely be served via the proposed interfaces.
We have similar interfaces in ZF2, and use them for both our HTTP
client and for the MVC; the main difference with the MVC is that we
had to also build a way of populating / marshaling the request from
the request environment. In phly/http, this is done via
Phly\Http\RequestFactory::fromServer(), which expects $_SERVER and
(optionally) a request instance. This approach is nice as it allows
you to use a lightweight, general purpose message when desired, which
is completely decoupled from the actual PHP request environment (for
example, in testing, or to fire off additional sub-requests within
your application). I find the approach cleaner from an implementation
detail as well -- it's a clear separation of concerns.

<snip>

>> I've spoken with Paul, Phil, and Beau, and they are willing to sponsor if I act as Editor for the proposal.
>
> Seeing as there are already two sponsors (Phil and Beau) I don't think I'm actually needed; that is, unless one of them chooses to step down.

I could maybe use a Coordinator, per the by-laws. :)

>> - What do you think is missing from PSR-7 as it currently stands, and, more importantly, why?
>
> I think the finer focus on the the Request being "what PHP receives from the client" and the Response being "what PHP sends back to the client" is hugely important. As such I think there are likely to be some light modifications for convenient access to the execution environment, things like access to $_GET/POST/FILES/COOKIE, perhaps $_ENV, and of course $_SERVER elements that are not specifically HTTP-related. Sending of cookies in the Response may become important as well.

In some ways, these don't _need_ to be addressed directly by the HTTP
message interfaces. In Node, for example, you will often write
middleware that grabs information out of the raw request and parses it
(e.g., to find and parse cookie values; to parse the query string; to
parse the incoming request body). In other words, this could be baked
into a decorator instead.

However, if we change the focus of PSR-7 to be specifically for
server-side applications, I think it's a reasonable fit -- but perhaps
instead of baking it into the RequestInterface, it could be in a
separate interface such as IncomingRequestInterface. This would allow
the basic RequestInterface to be happily agnostic of its use case, but
for concrete implementations to provide the additional functionality
for retrieving these PHP environment artifacts.

If so, I'd propose:

- getQueryArgument($name = null, $default = null)
- getBodyParam($name = null, $default = null)
- getCookie($name = null, $default = null)
- getFile($name = null, $default = null)
- getEnv($name = null, $default = null)
- getServer($name = null, $default = null)

and then related setters.

The problem with this is it's starting to look like a LOT of methods,
and thus a lot of implementation details, particularly when most of
these are working with superglobals. (In particular, getBodyParam() is
subject to problems due to differing body content types; I definitely
don't want developers to assume that any given implementation has
content negotiation baked in.)

When I've played with the interfaces as they currently stand, while
I've missed accessors for these, I've been able to deal with them via
middleware typically. In Conduit, I decorate the Request object to
allow setting arbitrary properties, which means middleware can grab
the information it needs, potentially from superglobals, and then
inject into well-known properties on the Request instance. I can see a
lightweight IncomingRequestInterface implementation not doing any
negotiation of the superglobals at all, and leaving the values to
either a factory or middleware to populate.

<snip>

>> - Have you built anything with middleware before, in any language? If so, what feedback do you have?
>
> No, and no feedback other than middleware is just another way of doing things, and that the renewed focus on request/response relating specifically to the server-side interaction should not preclude other ways of doing things.

Makes sense to me. I raise middleware as an example of one type of
interoperability PSR-7 would enable. Another is more simple exchange
of request/response between different framework layers. As an example,
in zf-oauth2, we delegate to oauth2-server-php -- and this would be so
much simpler if we could just pass our request/response pair to it
directly; instead, we end up having to negotiate between the two,
which can get quite complex. PSR-7 has the potential to simplify such
interactions tremendously.

--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 1:12:40 PM9/26/14
to php...@googlegroups.com
On Fri, Sep 26, 2014 at 11:38 AM, Evert Pot <ever...@gmail.com> wrote:
> On Friday, September 26, 2014 5:10:57 PM UTC+1, Matthew Weier O'Phinney
> wrote:
>> What I want to hear from YOU is:
>>
>> - What do you think is missing from PSR-7 as it currently stands, and,
>> more importantly, why?
>>
>> - Have you built anything with the interfaces as they currently stand?
>> What were your experiences?
>> - Have you built anything with middleware before, in any language? If so,
>> what feedback do you have?
>
>
> I implemented/borrowed from PSR-7, but only partly, here:
>
> https://github.com/fruux/sabre-http/
>
> I chose to ignore the stream object entirely, because for my purposes I need
> to have access to a stream resource.
> This is my one and only issue with the spec as it stands.
>
> 1. I need to be able to call file_put_contents('php://output',
> $response->getBody()); // or equivalent

This can be done already, more simply as:



> 2. I want to be able to throw streams into stream_select(), or even
> libevent.
> 3. I want XMLWriter to write directly to a stream.
> 4. I want to be able to read from incoming HTTP requests and throw it in
> (any) parser that can accept streams as well as strings.
>
> So if a 'Stream' object still exists, I can live with that, but for this to
> work efficiently for me I need some way to access the underlying stream
> resource is a standard manner.
>
> PHP Stream resources for better or worse are *the* standard way to do this,
> and we should use this standard and not invent our own inferior version of
> it.
>
> Good luck!
> Evert
>
> --
> 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/CTPRa2XP8po/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/91c7088a-f15f-49d7-8e1d-1fd84889b62e%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.



--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 1:19:13 PM9/26/14
to php...@googlegroups.com
Sorry for that last post... hit some wierd key combo that sent the
email as I was mid-sentence...

On Fri, Sep 26, 2014 at 11:38 AM, Evert Pot <ever...@gmail.com> wrote:
> On Friday, September 26, 2014 5:10:57 PM UTC+1, Matthew Weier O'Phinney
> wrote:
>> What I want to hear from YOU is:
>>
>> - What do you think is missing from PSR-7 as it currently stands, and,
>> more importantly, why?
>>
>> - Have you built anything with the interfaces as they currently stand?
>> What were your experiences?
>> - Have you built anything with middleware before, in any language? If so,
>> what feedback do you have?
>
>
> I implemented/borrowed from PSR-7, but only partly, here:
>
> https://github.com/fruux/sabre-http/
>
> I chose to ignore the stream object entirely, because for my purposes I need
> to have access to a stream resource.
> This is my one and only issue with the spec as it stands.
>
> 1. I need to be able to call file_put_contents('php://output',
> $response->getBody()); // or equivalent

This can be done already, without even needing to call file_put_contents():

$response->setBody(new Stream('php://output'));

That tells the response to use the new stream. When you send it
(usually by calling __toString() or casting to a string), it will
essentially do what you've already described.

> 2. I want to be able to throw streams into stream_select(), or even
> libevent.

You can do this already -- Stream accepts any PHP stream to the
constructor. If you already have the resource handle, you can pass it
around. Alternately, you can do the following:

$resource = $response->getBody()->detach();
// pass it to stream_select, etc.
$response->setBody(new Stream($resource));

> 3. I want XMLWriter to write directly to a stream.

Same pattern as above.

> 4. I want to be able to read from incoming HTTP requests and throw it in
> (any) parser that can accept streams as well as strings.

Same idea:

$incoming = $request->getBody()->detach();
// pass it to your parser

> So if a 'Stream' object still exists, I can live with that, but for this to
> work efficiently for me I need some way to access the underlying stream
> resource is a standard manner.

Hopefully what I've outlined above answers your questions -- if not,
let me know why not. :)

--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Evert Pot

unread,
Sep 26, 2014, 1:23:34 PM9/26/14
to php...@googlegroups.com

>
> I chose to ignore the stream object entirely, because for my purposes I need
> to have access to a stream resource.
> This is my one and only issue with the spec as it stands.
>
> 1. I need to be able to call file_put_contents('php://output',
> $response->getBody()); // or equivalent

This can be done already, without even needing to call file_put_contents():

    $response->setBody(new Stream('php://output'));

That tells the response to use the new stream. When you send it
(usually by calling __toString() or casting to a string), it will
essentially do what you've already described.

Except that file_put_contents can accept streams as its second argument. I'm dealing with responses from megabytes to multiple gigabytes.
You'll really don't want to put those in a string.
 

> 2. I want to be able to throw streams into stream_select(), or even
> libevent.

You can do this already -- Stream accepts any PHP stream to the
constructor. If you already have the resource handle, you can pass it
around. Alternately, you can do the following:

    $resource = $response->getBody()->detach();
    // pass it to stream_select, etc.
    $response->setBody(new Stream($resource));

In the current draft 'detach' does not have a return value. If it did, and it always returned a PHP stream then I have no problem at all and 100% on board with the draft as-is.

Evert

Evert Pot

unread,
Sep 26, 2014, 1:27:32 PM9/26/14
to php...@googlegroups.com


If so, I'd propose:

- getQueryArgument($name = null, $default = null)
- getBodyParam($name = null, $default = null)
- getCookie($name = null, $default = null)
- getFile($name = null, $default = null)
- getEnv($name = null, $default = null)
- getServer($name = null, $default = null)


I'm a big fan of these, but I would love it even more if that use-case could be addressed in a future PSR, and have one great core interface that addresses both the server and client use-case.

Cheers,
Evert

Cal Evans

unread,
Sep 26, 2014, 1:31:44 PM9/26/14
to php...@googlegroups.com


On Fri, Sep 26, 2014 at 11:10 AM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:
Greetings!

You had me at Greetings! :)

Do it. Sounds great. I totally agree with the importance of this.

Cheers!
=C=


--
Signaling PHP
Learn to catch signals in PHP

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 1:35:12 PM9/26/14
to php...@googlegroups.com
On Fri, Sep 26, 2014 at 12:23 PM, Evert Pot <ever...@gmail.com> wrote:
>> > I chose to ignore the stream object entirely, because for my purposes I
>> > need
>> > to have access to a stream resource.
>> > This is my one and only issue with the spec as it stands.
>> >
>> > 1. I need to be able to call file_put_contents('php://output',
>> > $response->getBody()); // or equivalent
>>
>> This can be done already, without even needing to call
>> file_put_contents():
>>
>> $response->setBody(new Stream('php://output'));
>>
>> That tells the response to use the new stream. When you send it
>> (usually by calling __toString() or casting to a string), it will
>> essentially do what you've already described.
>
>
> Except that file_put_contents can accept streams as its second argument. I'm
> dealing with responses from megabytes to multiple gigabytes.
> You'll really don't want to put those in a string.

Oh, silly me -- I missed that both arguments were strings! In this
case, I'd create the stream, do the file_put_contents() operation, and
then pass the new stream into the response:

$bodyStream = fopen('...', 'rw');
file_put_contents('php://output', $bodyStream);
$response->setBody(new Stream($bodyStream));


>> > 2. I want to be able to throw streams into stream_select(), or even
>> > libevent.
>>
>> You can do this already -- Stream accepts any PHP stream to the
>> constructor. If you already have the resource handle, you can pass it
>> around. Alternately, you can do the following:
>>
>> $resource = $response->getBody()->detach();
>> // pass it to stream_select, etc.
>> $response->setBody(new Stream($resource));
>
>
> In the current draft 'detach' does not have a return value. If it did, and
> it always returned a PHP stream then I have no problem at all and 100% on
> board with the draft as-is.

Michael had made it clear in his blog post making a case for streams
that this was the intention, which is why I implemented it that way in
phly/http. I'll update the proposal to reflect this!

Michael Dowling

unread,
Sep 26, 2014, 1:40:07 PM9/26/14
to php...@googlegroups.com
Glad to hear that you'll be taking this on, Matthew. I didn't realize someone actually liked the proposal... would have been nice to hear from you sooner :)
  
- Have you built anything with the interfaces as they currently stand? What were your experiences?

The interfaces worked great for me :)


I have a couple suggestions for the existing proposal:

1. Remove the maxLength parameter from the getContents method of StreamInterface. This added a lot of unnecessary complexity when implementing decorators over streams when it didn't have to. You can do the maxLength stuff by just using read().
2. Add a getMetadata() function to StreamInterface. This allows arbitrary information to be attached to a stream when they are created (not mutable after creation). There shouldn't be a mandate on what can or can't be in the metadata: https://github.com/guzzle/streams/blob/3.0/src/StreamInterface.php#L157.
3. I'm not sure if the change was ever made to the interfaces, but I suggest removing the fluent interfaces from the messages. This has been discussed to death, and I agree with the people who say they should be removed.
 
- Have you built anything with middleware before, in any language? If so, what feedback do you have?

Middleware and HTTP messages aren't related IMO. And yes, I've worked with middleware in other languages, and the interesting this is that a lot of other languages don't need a language-wide spec to drive consensus on a project. Clojure-ring for example, is not officially sanctioned by Clojure-- it's just the most popular middleware implementation in Clojure because it's the best. The same thing happened in Ruby with Rack. Rack has become a de-facto standard in Ruby because it gained adoption-- not because a standards body pushed it forward.

Anyways, I built this recently: https://github.com/guzzle/guzzle-ring. It's a middleware system inspired by Clojure's Ring (https://github.com/ring-clojure/ring), and therefore favors functional composition and simple hashes over classes and objects that wrap HTTP messages. The extreme simplicity of this design has proven to be very flexible, fast, and supports asynchronous requests through futures. I even wonder if PSR-7 should be more like this library (a specification about request and responses hashes) than a set of interfaces: http://guzzle-ring.readthedocs.org/en/latest/spec.html

-Michael

Evert Pot

unread,
Sep 26, 2014, 1:43:45 PM9/26/14
to php...@googlegroups.com

> Except that file_put_contents can accept streams as its second argument. I'm
> dealing with responses from megabytes to multiple gigabytes.
> You'll really don't want to put those in a string.

Oh, silly me -- I missed that both arguments were strings! In this
case, I'd create the stream, do the file_put_contents() operation, and
then pass the new stream into the response:

    $bodyStream = fopen('...', 'rw');
    file_put_contents('php://output', $bodyStream);
    $response->setBody(new Stream($bodyStream));

This is a bit.. weird. Not sure if that would work :)

But the more important point is, that the library that sends the response (Server::send()) in your case, may not be library that first generates it.

if your Phly\Http\Server class receives a Response object that it's supposed to return to the client, and it's a multi-gigabyte one, how would it behave?

With detach() this would work of course :)


>> > 2. I want to be able to throw streams into stream_select(), or even
>> > libevent.
>>
>> You can do this already -- Stream accepts any PHP stream to the
>> constructor. If you already have the resource handle, you can pass it
>> around. Alternately, you can do the following:
>>
>>     $resource = $response->getBody()->detach();
>>     // pass it to stream_select, etc.
>>     $response->setBody(new Stream($resource));
>
>
> In the current draft 'detach' does not have a return value. If it did, and
> it always returned a PHP stream then I have no problem at all and 100% on
> board with the draft as-is.

Michael had made it clear in his blog post making a case for streams
that this was the intention, which is why I implemented it that way in
phly/http. I'll update the proposal to reflect this!

There's a related question to detach() here.. It would be great if I can later on attach it again.
Or better yet, provide a method that returns the stream, without detaching it.

Sometimes we want to go over the entire stream (integrity checks for instance) and if the stream is seekable, just put the cursor back on byte 0.

http://php.net/manual/en/function.hash-update-stream.php

It would be possible to construct a new stream object, but it feels a bit clumsy to me :)

Evert

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 2:59:56 PM9/26/14
to php...@googlegroups.com
On Fri, Sep 26, 2014 at 12:40 PM, Michael Dowling <mtdo...@gmail.com> wrote:
> Glad to hear that you'll be taking this on, Matthew. I didn't realize
> someone actually liked the proposal... would have been nice to hear from you
> sooner :)

Well, I was just ramping up to give feedback to you (I sent you one
PR, as you recall!) when you decided to stop pushing the proposal! In
other words, it's all timing!


>> - Have you built anything with the interfaces as they currently stand?
>> What were your experiences?
>
>
> The interfaces worked great for me :)
>
> 1. http://guzzlephp.org
> 2. https://github.com/guzzle/streams

Yes, of course I saw these. :) Although, to be fair, you're only
really doing the stream interface, correct? The Guzzle
request/response interfaces look quite different from PSR-7 (though
their underlying MessageInterface is basically identical). Or am I
missing something?

I considered trying to use these briefly, but they are definitely
geared more toward the client, and ease of creating requests and
parsing responses, from what I could see. PSR-7 feels like a layer
that would lie underneath that.

> I have a couple suggestions for the existing proposal:
>
> 1. Remove the maxLength parameter from the getContents method of
> StreamInterface. This added a lot of unnecessary complexity when
> implementing decorators over streams when it didn't have to. You can do the
> maxLength stuff by just using read().

Yeah, that was a pain to implement, and I can't say I'd ever use it.
Making a note to do a PR for that.

> 2. Add a getMetadata() function to StreamInterface. This allows arbitrary
> information to be attached to a stream when they are created (not mutable
> after creation). There shouldn't be a mandate on what can or can't be in the
> metadata:
> https://github.com/guzzle/streams/blob/3.0/src/StreamInterface.php#L157.

Considering that StreamInterface is designed to abstract many of the
most common stream operations, this makes sense.

> 3. I'm not sure if the change was ever made to the interfaces, but I suggest
> removing the fluent interfaces from the messages. This has been discussed to
> death, and I agree with the people who say they should be removed.

Yes -- the change was made already (all setters now either have a
`@return void` or omit the `@return` annotation.

>> - Have you built anything with middleware before, in any language? If so,
>> what feedback do you have?
>
>
> Middleware and HTTP messages aren't related IMO.

I was talking specifically of HTTP middleware. And the reason I think
the messages are important here is that they provide a common contract
for what a request and response look like and how they behave. No more
parsing $_SERVER to get at headers (and trying to remember which ones
do not follow the `HTTP_*` naming), no more guessing at where the URI
might be (is it in REQUEST_URI? UNENCODED_URL? HTTP_X_REWRITE_URL?
HTTP_X_ORIGINAL_URL? ORIG_PATH_INFO? Did you even know these were all
possibilities? -- not you, specifically, Michael -- I'm talking the
general PHP developer).

So, I have to disagree with you here that the messages aren't related
to HTTP-specific middleware. They make HTTP-specific middleware
interoperable -- which is the point of FIG, after all. This goes back
to my observations on the success of Node -- because these are baked
in as core modules of Node, it's what all HTTP frameworks use, which
means everything is interoperable. That's the goal I have.

> And yes, I've worked with
> middleware in other languages, and the interesting this is that a lot of
> other languages don't need a language-wide spec to drive consensus on a
> project. Clojure-ring for example, is not officially sanctioned by Clojure--
> it's just the most popular middleware implementation in Clojure because it's
> the best. The same thing happened in Ruby with Rack. Rack has become a
> de-facto standard in Ruby because it gained adoption-- not because a
> standards body pushed it forward.

Correct. I also want to point out that while I'm definitely calling
out middleware as a possibility here, it's one of several
possibilities for PSR-7. I think it's something that is likely to grow
out of it, because with framework-agnostic message interfaces, we'll
start seeing developers making HTTP-based projects that build on them
as the common denominator, because that will allow their code to
interoperate regardless of which framework the consumer is using.
Middleware grows out of that sort of ecosystem. But it's not the point
of the proposal; the point of the proposal is to cleanly and clearly
define HTTP messages that can be re-used across projects. How that
re-use happens is up to developers.

> Anyways, I built this recently: https://github.com/guzzle/guzzle-ring. It's
> a middleware system inspired by Clojure's Ring
> (https://github.com/ring-clojure/ring), and therefore favors functional
> composition and simple hashes over classes and objects that wrap HTTP
> messages. The extreme simplicity of this design has proven to be very
> flexible, fast, and supports asynchronous requests through futures. I even
> wonder if PSR-7 should be more like this library (a specification about
> request and responses hashes) than a set of interfaces:
> http://guzzle-ring.readthedocs.org/en/latest/spec.html

I'd argue that an interface _is_ a specification. The difference is
one of _type_, with an interface being a specification for objects.
Objects are of interest for this particular specification as they can
allow for behaviors you cannot get with arrays:

- I can decorate an object in order to change behavior. As an example,
I can make a Request or Response immutable via decoration.
- I can filter what is returned from an object through extension or
decoration; the same is not true of an array.
- I can add behavior via decoration or extension (as an example, in
Conduit, I made it possible to add arbitrary properties to the Request
object by decorating it; I added a "write" method to the Response that
proxies to the underlying stream, also via decoration).

While I think the Ring specification is well done, I think for
purposes of interop, interfaces will cause less confusion and less
interoperability issues.


--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 3:15:23 PM9/26/14
to php...@googlegroups.com
On Fri, Sep 26, 2014 at 12:43 PM, Evert Pot <ever...@gmail.com> wrote:
>> > Except that file_put_contents can accept streams as its second argument. I'm
>> > dealing with responses from megabytes to multiple gigabytes.
>> > You'll really don't want to put those in a string.
>>
>> Oh, silly me -- I missed that both arguments were strings! In this
>> case, I'd create the stream, do the file_put_contents() operation, and
>> then pass the new stream into the response:
>>
>> $bodyStream = fopen('...', 'rw');
>> file_put_contents('php://output', $bodyStream);
>> $response->setBody(new Stream($bodyStream));
>
>
> This is a bit.. weird. Not sure if that would work :)
>
> But the more important point is, that the library that sends the response (Server::send()) in your case, may not be library that first generates it.

That's a very specific implementation -- I think if you're doing
applications where streaming is at the foundation, I'd likely do a
different server implementation!

> if your Phly\Http\Server class receives a Response object that it's supposed to return to the client, and it's a multi-gigabyte one, how would it behave?
>
> With detach() this would work of course :)

And that's how I'd likely handle it in such situations. But that's an
implementation detail for the consumer -- the underlying interface is
still sane -- and with features such as detach(), you have
fine-grained control over how you manage the stream when returning it
to the consumer.

>> >> > 2. I want to be able to throw streams into stream_select(), or even
>> >> > libevent.
>> >>
>> >> You can do this already -- Stream accepts any PHP stream to the
>> >> constructor. If you already have the resource handle, you can pass it
>> >> around. Alternately, you can do the following:
>> >>
>> >> $resource = $response->getBody()->detach();
>> >> // pass it to stream_select, etc.
>> >> $response->setBody(new Stream($resource));
>> >
>> >
>> > In the current draft 'detach' does not have a return value. If it did, and
>> > it always returned a PHP stream then I have no problem at all and 100% on
>> > board with the draft as-is.
>>
>> Michael had made it clear in his blog post making a case for streams
>> that this was the intention, which is why I implemented it that way in
>> phly/http. I'll update the proposal to reflect this!
>
>
> There's a related question to detach() here.. It would be great if I can later on attach it again.
> Or better yet, provide a method that returns the stream, without detaching it.

Michael made the case that if you get direct access to the stream,
there's a possibility that the StreamInterface implementation instance
and the underlying stream then go out-of-sync -- which is why he
proposed that detach() should remove the underlying resource
completely. (See the heading "Getting the underlying PHP stream from a
StreamInterface" here:
http://mtdowling.com/blog/2014/07/03/a-case-for-higher-level-php-streams/)

That said, I think if an attach() method were present that basically
reset any stored state related to the Stream as part of its work,
those issues would be addressed.

> Sometimes we want to go over the entire stream (integrity checks for instance) and if the stream is seekable, just put the cursor back on byte 0.

Are you talking about reading the entire stream in chunks, and then
resetting the cursor? If so, that's already possible:

while (! $stream->eof()) {
$chunk = $stream->read(4096);
// do something with the chunk
}
$stream->seek(0);

However, I think what you're getting at is more in the line of being
able to pipe the underlying PHP stream to another function, ala:

> http://php.net/manual/en/function.hash-update-stream.php

In that case, Michael's article provides another solution: converting
the Psr\Http\StreamInterface instance into a full-fledged PHP stream:

// Create a PHP stream from the Guzzle stream that just wraps the
Guzzle stream
$resource = GuzzleStreamWrapper::getResource($stream);

Essentially, at this point, you have a PHP stream you can then pass to
any object. The code for this is trivial (see
https://github.com/guzzle/streams/blob/master/src/GuzzleStreamWrapper.php),
and would be easy to add to a reference implementation. I think it
falls outside of PSR-7, but would largely address the use cases you've
suggested. In fact, i think it also applies to that
file_put_contents($stream1, $streamInterfaceImplementation) use case.

For the time being, I'm making a note to add attach() to the proposal.

Michael Dowling

unread,
Sep 26, 2014, 3:47:48 PM9/26/14
to php...@googlegroups.com
Yes, of course I saw these. :) Although, to be fair, you're only 
really doing the stream interface, correct? The Guzzle 
request/response interfaces look quite different from PSR-7 (though 
their underlying MessageInterface is basically identical). Or am I 
missing something? 

I considered trying to use these briefly, but they are definitely 
geared more toward the client, and ease of creating requests and 
parsing responses, from what I could see. PSR-7 feels like a layer 
that would lie underneath that. 

No, I implemented the PSR-7 interfaces almost exactly (both messages and streams). The interfaces in Guzzle are almost exactly the same as PSR-7 with a couple of additional methods. See https://github.com/guzzle/guzzle/blob/master/src/Message/MessageInterface.php, https://github.com/guzzle/guzzle/blob/master/src/Message/RequestInterface.php, and https://github.com/guzzle/guzzle/blob/master/src/Message/ResponseInterface.php.

 I'd argue that an interface _is_ a specification. The difference is 
one of _type_, with an interface being a specification for objects. 
Objects are of interest for this particular specification as they can 
allow for behaviors you cannot get with arrays: 
- I can decorate an object in order to change behavior. As an example, 
I can make a Request or Response immutable via decoration. 
- I can filter what is returned from an object through extension or 
decoration; the same is not true of an array. 
- I can add behavior via decoration or extension (as an example, in 
Conduit, I made it possible to add arbitrary properties to the Request 
object by decorating it; I added a "write" method to the Response that 
proxies to the underlying stream, also via decoration). 
While I think the Ring specification is well done, I think for 
purposes of interop, interfaces will cause less confusion and less 
interoperability issues.  

I disagree with your claims. This is a bit of a side conversation, but since you brought up middleware, I suppose it can be discussed. The point of Rack, Ring, and other hash based middleware is that you define request and response messages using hashes and create a response hash for a request hash using a function. The specification on sending or responding to a request (for both client and server) is that you have a callable that accepts a request hash and returns a response hash. There are usually some required keys, but middleware is free to add any keys as needed. This very simple contract can do powerful things, eliminates the need for us to design message interfaces, and provides a client interface (a function) at the same time. To add behavior or extensions to this design is simply a matter of functional composition-- wrapping the simple function with another function that modifies the request and response hash as they pass through.

> I can decorate an object in order to change behavior. As an example, I can make a Request or Response immutable via decoration. 

A request and response array are immutable by default (unless you pass them somewhere by reference). You can also wrap (or decorate) the function that transfers the requests by adding things to incoming request and even decorating the response hash that's returned from the function after the underlying function has returned a response hash. Want to add a header to each request? Wrap your function in another function that adds the header to the request hash and passes it through to the wrapped function.

> I can filter what is returned from an object through extension or decoration; the same is not true of an array.

Yes, it is true with an array because you can decorate the function that transfers the request and returns the response. This allows you to modify both the request and the response as it passes through a function. See https://github.com/guzzle/guzzle-ring/blob/master/src/Client/Middleware.php

> While I think the Ring specification is well done, I think for purposes of interop, interfaces will cause less confusion and less interoperability issues.  

Thanks. To each their own. What I did with the ring stuff isn't necessarily at odds with this proposal either-- it's meant to work at a lower level (for example, I am going to use it in the next version of Guzzle: https://github.com/guzzle/guzzle/blob/ring/src/RingBridge.php).

I just wanted to point out that many popular middleware implementations in other languages have a much simpler design that just uses hashes and a specification (see Clojure Ring, Ruby Rack, etc.).

Matthew Weier O'Phinney

unread,
Sep 26, 2014, 4:18:58 PM9/26/14
to php...@googlegroups.com
On Fri, Sep 26, 2014 at 2:47 PM, Michael Dowling <mtdo...@gmail.com> wrote:
>> Yes, of course I saw these. :) Although, to be fair, you're only
>> really doing the stream interface, correct? The Guzzle
>> request/response interfaces look quite different from PSR-7 (though
>> their underlying MessageInterface is basically identical). Or am I
>> missing something?
>>
>>
>> I considered trying to use these briefly, but they are definitely
>> geared more toward the client, and ease of creating requests and
>> parsing responses, from what I could see. PSR-7 feels like a layer
>> that would lie underneath that.
>
>
> No, I implemented the PSR-7 interfaces almost exactly (both messages and
> streams). The interfaces in Guzzle are almost exactly the same as PSR-7 with
> a couple of additional methods. See
> https://github.com/guzzle/guzzle/blob/master/src/Message/MessageInterface.php,
> https://github.com/guzzle/guzzle/blob/master/src/Message/RequestInterface.php,
> and
> https://github.com/guzzle/guzzle/blob/master/src/Message/ResponseInterface.php.

It was the additional methods that I was seeing -- a lot of stuff in
there for getting at URI parts (e.g., getHost, getPort, getScheme,
getPath,) or parsing the request body (e.g. json(), xml()). Those
threw me off. But yes -- other than the additions, they are 1:1
compatible -- and it would be easy to create a decorator of a PSR-7
request that could then implement those methods defined in the Guzzle
interfaces.

<snip>

> I just wanted to point out that many popular middleware implementations in
> other languages have a much simpler design that just uses hashes and a
> specification (see Clojure Ring, Ruby Rack, etc.).

Technically, this is true of Node as well -- JavaScript doesn't really
have typed objects; you test type by comparing an object to a
constructor function that would yield that object. That said, the
http.IncomingMessage and http.ServerResponse also have behavior built
into them via methods, which makes them more than just hashes.

I agree that we could make a specification based on arrays. I don't
see people _adopting_ that specification in PHP, however, due to
wanting a contract (interface) to test against. Maybe that will change
in PHP 7 if we finally get the ability to import functions just like
we do classes, and people start adopting functional programming
styles; until then, considering most projects in FIG are object
oriented, I think for practical purposes, interfaces stand a greater
likelihood for adoption.

Paul M. Jones

unread,
Sep 26, 2014, 4:20:00 PM9/26/14
to php...@googlegroups.com

On Sep 26, 2014, at 3:18 PM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:

> considering most projects in FIG are object
> oriented, I think for practical purposes, interfaces stand a greater
> likelihood for adoption.

Hear, hear.

Hari K T

unread,
Sep 26, 2014, 11:16:22 PM9/26/14
to php...@googlegroups.com
Thank you Matthew, you did an awesome job introducing.

When I hear Middleware the one thing that I always recall is the Slim https://github.com/codeguy/Slim/blob/master/Slim/Middleware.php . I have looked into it and tried to bring that in Aura.

I also have wondered whether we need a middleware when there are events and have asked the same with him. Now I find the real answer :-) .

Beau did introduced the Stack to me. But it wasn't digestible at the time of discussion when we need to rely on symfony/http-foundation entirely for just an interface ( https://github.com/symfony/HttpKernel/blob/master/HttpKernelInterface.php ) .

After long time looking at his slides today of the Symfony Live London ( https://beau.io/talks/2014/09/26/decorating-applications-with-stack-symfony-love-london-2014/?ref=ddd ) I was impressed with the concept and I feel why a middleware will help the whole php community.

Some of the middleware implementations I noticed apart from your work on conduit is   https://github.com/odino/exphpress from Alessandro Nadalin . And he is here in the group and I am sure he will be pinging on the same.

Thank you once again for trying to push PSR-7

Hari K T

You can ring me : +91 9388 75 8821

Skype  : kthari85
Twitter : harikt

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

Chris Wilkinson

unread,
Sep 27, 2014, 3:14:38 AM9/27/14
to php...@googlegroups.com
Good to see this revived! I've been thinking about these interfaces again recently too, and I saw Beau’s talk yesterday (didn't have a chance to say hello after unfortunately!) which I think has helped solidify my opinions here. I think it tries to do far more than is needed (my original proposal definitely suffered from this too).

I've thought back to what I feel this proposal is trying to achieve: the ability to share PHP representations of HTTP requests and responses. We have a way to do this already, without doing anything: using plain strings. This generally works, but clearly has problems (having to parse it into something usable (object or array) is a pain, let alone memory problems with large messages). So, this is what the proposal should try and solve, and that’s it. This means two interfaces (request and response), with getters for all the parts of the message, and nothing more. The body should a string or something that can be cast to a string; headers treated as a set of key - string value pairs.

There should not be a MessageInterface (there’s really no need for it), and there should not be a body, stream or whatever interface. There should, however, be a separate PSR for a stream-wrapper interface (separating out the proposed one looks fine to me), which as long as it enforces __toString() solves the problem (so a naïve HTTP client/server doesn't have to worry about it if they don’t want to - and probably won't work when trying to send a 10GB file - whereas a smart one can check or it and handle it accordingly).

It terms of how it would be used, it would probably mean that implementations have static factory methods for creating it from the PSR version, and this would probably generally only happen at integrations points, so the library/framework/whatever can do whatever it needs to do with its own version. So I foresee things like:

function send(RequestInterface $request) {
    if(!$request instanceof MyRequest) {
        $request = MyRequest::createFromRequest($request)
    }
    // do stuff
}

Then it might pass it to an adapter typehinted as MyRequest rather than RequestInterface, which is all perfectly fine as the PSR has achieved what it was designed to do. (For current implementations where the typehint wouldn't be changed to maintain BC, the user can turn it into the right form beforehand, which also works - implementations could also choose to rely on this in the future if they want.)

Chris

Matthew Weier O'Phinney

unread,
Sep 27, 2014, 9:10:12 AM9/27/14
to php...@googlegroups.com


On Sep 27, 2014 2:14 AM, "Chris Wilkinson" <chriswil...@gmail.com> wrote:
>  So, this is what the proposal should try and solve, and that’s it. This means two interfaces (request and response), with getters for all the parts of the message, and nothing more. The body should a string or something that can be cast to a string; headers treated as a set of key - string value pairs.
>
> There should not be a MessageInterface (there’s really no need for it), and there should not be a body, stream or whatever interface.

The MessageInterface exists to abstract out the  aspects common to both requests and responses (in fact, the http specification itself does this! See http://tools.ietf.org/html/rfc2616 ). You will rarely, if ever, typehint on that interface; you'll instead typehint on the more specific request or response interfaces, which _extend_ it. But it needs to be there.

While I understand some of the arguments against an interface for the body type, I personally feel they are short-sighted. For http clients, this becomes a memory issue: sending or receiving large files within a php process is very common for these purposes, and something that just casts to a string will quickly reach memory limits... When representation as a stream will solve the problem. On the server side, eventually somebody wants to return a file - and, again, if your body is only representable as a string, you hit memory limits when you cast to a string, leading to a need to have separate response does for files and separate APIs for dealing with responses that will return files. Similarly, using a string for a request can lead to issues when dealing with file uploads.

Using a stream-based body for http messages as the default since these issues. You can cast to a string if desired, **or** treat it as a stream - but it's the same API every time.

Most hesitation I've seen about using streams is, in my opinion, due to unfamiliarity with streams and/or worries that implementations will be difficult to write. My answer to that question is: please look at my reference implementation (https://github.com/phly/http) and am example of using it (http://github.com/phly/conduit). Requests can directly wrap php://input - a stream - and the simplest way to do a response wraps a php://memory stream. As a consumer, these are implementation details you largely ignore... Until you need to send a file or want to pass the request body to XMLReader, at which point you can do so without any extra overhead or code acrobatics.

It's for these same reasons that node models the message bodies as streams as well. The paradigm enables any use case, from simple to advanced.

I stand firmly by streams as the basis for the message bodies.

Larry Garfield

unread,
Sep 27, 2014, 3:46:22 PM9/27/14
to php...@googlegroups.com
On 09/26/2014 10:19 PM, Paul M. Jones wrote:
> On Sep 26, 2014, at 3:18 PM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:
>
>> considering most projects in FIG are object
>> oriented, I think for practical purposes, interfaces stand a greater
>> likelihood for adoption.
> Hear, hear.

As the representative from the most Array-Oriented member project
(Drupal 7), I want to go on record as saying "screw arrays!" Interfaces
all the way, baby. :-)

--Larry Garfield

Larry Garfield

unread,
Sep 27, 2014, 4:23:20 PM9/27/14
to php...@googlegroups.com
Rather than reply to selected messages I will just bulk reply here.

tldr: Woohoo!

Full version:

1) I've made no secret of my belief that a common
Request/Response/Kernel[middleware] is something PHP long term needs.
While I like the model Symfony has, as others have noted the need to
bring in all of HttpFoundation and HttpKernel just for a few interfaces
and an object or two is prohibitively excessive for many. It is my
explicit hope and intent that long term PSR-7 can be the thin baseline
around which a PHP-wide standard middleware mechanism can be built
(Stack TNG, basically, whether FIG-specced or not), which would benefit
everyone. It sounds like we're on the same page there.

In particular:

"In a nutshell: what I dream of for PHP is that we stop thinking of
ourselves as Zend Framework, or Symfony, or Laravel, or Aura, or
FrameworkFlavorOfTheDay developers, but instead think of ourselves as
PHP, HTTP, or Web developers."

MWOP, would you get out of my dreams? :-)

2) Regarding client vs. server use cases, I agree that they're not as
easy to square as we'd like, mainly around the body. However, I am
concerned about ignoring the client case. Not as much for actual
clients as for testing. One of the benefits of a fully abstracted
message and kernel/middleware system is that you can create a request
from whole cloth as part of a test, pass it through the system, and get
back a response; you have not fired a single real HTTP request but no
code can tell the difference. That is, your test is an HTTP client (or
has one), and it treats your app as a server, all within a single PHP
process. That's very powerful and we should try to support that case if
at all possible. It's a good "canary" use case, I feel, for getting it
"right". That said, I do agree that if we're forced to choose that the
server-centric use case has the bigger potential impact on the PHP world.

3) Repeating and summarizing my review of the current draft from when it
was last discussed, there are three use cases for the body to consider:

A) String
B) Stream (including files)
C) Callable (eg, generating 1 millions lines of CSV out of a database)

As MWOP and others have noted, String is easily representable as a
Stream so that's a non-issue. But representing Callable and Stream
together is much more challenging and may require an extra interface. I
was discussing that with Beau a few weeks ago over lunch but I forget
the details at this hour. However, we should try to ensure all of those
are possible as callable is a very useful use case.

4) My main issue with StreamInterface (or now Streamable, whatever) is
that it's not wrapping PHP native streams but reimplementing them.
Especially the stacking/decorating concept is really just reimplementing
PHP native stream behavior, but slowly. (Without knowing if a Stream is
really a native stream, even just printing output requires taking the
slowest approach possible in PHP.) I agree that the PHP native stream
API bites, but it is the most powerful option we have.

My recommendation at the time was to stop pretending that
StreamInterface was anything other than a convenience wrapper around
real PHP streams, and then build its API on that assumption; it ALWAYS
contains a stream resource, period, and since we know that we can do
things like attach a filter or whatnot without any extra work. (And
that means all existing PHP stream wrappers or stream filters are
automatically supported. Spiffy!) I still believe that is the optimal
way forward here.

5) The current spec does not include a breakdown of the URI, true.
However, it does support passing or returning a __toString()-able
object. That was added specifically to support a dedicated object to
represent a URI, on which we could also have various methods for getting
path parameters, etc. There was some time ago a proposal for a URI
interface. It was quite good I thought but the submitter never kept up
with it. I believe that, rather than adding more methods to the
MessageInterface, is the best way to handle the URI portion of the
message. It's complex enough to warrant its own dedicated
interface/object. Whether we want to fold that into PSR-7 is debatable,
though. (Realistically it is an interface with only one meaningful
implementation.)

6) On the subject of mutability, I spoke out in favor of immutable
objects previously. However, for middlewares I can see where the
objects must be mutable. That, however, leads to the next question
which is where those objects store data being passed between
middlewares. In Symfony, there's the $request->attributes public bag
property. I quite hate it, to be honest, but I don't know of a good
alternative. That may be out of scope at the moment but is something we
should be keeping in mind if encouraging a middleware ecosystem is part
of the goal.

7) Oh yes, and death to anonymous arrays. :-)

--Larry Garfield

Matthew Weier O'Phinney

unread,
Sep 30, 2014, 10:12:25 AM9/30/14
to php...@googlegroups.com
On Sat, Sep 27, 2014 at 3:23 PM, Larry Garfield <la...@garfieldtech.com> wrote:
> On 09/26/2014 06:10 PM, Matthew Weier O'Phinney wrote:
> 2) Regarding client vs. server use cases, I agree that they're not as easy
> to square as we'd like, mainly around the body. However, I am concerned
> about ignoring the client case.

Actually, I think I need to make a clarification here.

The **reason** I want to see this proposal completed and ratified is
due to my belief that it will have immense server-side ramifications
in terms of interoperability.

The proposal is strictly (in its current form) detailing HTTP
messages. These are useful for **BOTH** server-side code **AND**
clients. I have validated the former (via
https://github.com/phly/conduit), while Michael has validated the
latter (via Guzzle).

My point here is that, as they stand right now, the interfaces
describe general-purpose HTTP messages, plain and simple, and those
are used in both sides of an HTTP request. And that answers your
following remark:

> Not as much for actual clients as for
> testing. One of the benefits of a fully abstracted message and
> kernel/middleware system is that you can create a request from whole cloth
> as part of a test, pass it through the system, and get back a response; you
> have not fired a single real HTTP request but no code can tell the
> difference. That is, your test is an HTTP client (or has one), and it
> treats your app as a server, all within a single PHP process. That's very
> powerful and we should try to support that case if at all possible. It's a
> good "canary" use case, I feel, for getting it "right".

This can be done already. In fact, I've done this when testing Conduit
itself -- I created request and response objects, and tested what I
had after executing middleware. It "just works" already.

> That said, I do
> agree that if we're forced to choose that the server-centric use case has
> the bigger potential impact on the PHP world.

I don't actually think we need to choose at this point. The interfaces
describe HTTP messages essentially per the HTTP RFC, and work on both
sides of the request. I think the server-side use case has bigger
potential for impact in PHP (clients, as great as they are, are a much
smaller segment of use cases within the PHP ecosystem), but that
doesn't change the fact they work in tandem.

> 3) Repeating and summarizing my review of the current draft from when it was
> last discussed, there are three use cases for the body to consider:
>
> A) String
> B) Stream (including files)
> C) Callable (eg, generating 1 millions lines of CSV out of a database)
>
> As MWOP and others have noted, String is easily representable as a Stream so
> that's a non-issue. But representing Callable and Stream together is much
> more challenging and may require an extra interface. I was discussing that
> with Beau a few weeks ago over lunch but I forget the details at this hour.
> However, we should try to ensure all of those are possible as callable is a
> very useful use case.

Callable is an interesting use case. Can you elaborate on how you
envision it being used? Would it be something along the lines of:

$response->setBody(function () {
// do some work and return a string
});

?

If that's the case, it may be something that can be wrapped in the
Streamable interface -- though it then begs that we call that
interface something entirely different, obviously. :) You could wrap
it by making the methods isReadable(), isWriteable(), and isSeekable()
hard-coded to return false, and all methods other than getContents()
and __toString() implemented as no-ops. As such, you'd have a use case
like this:

$response->setBody(new CallableStream(function () {
// do some work and return a string
}));

That said, what you're modelling is not a stream. Ideally we'd then
allow the body to be EITHER a stream OR a callable type -- but that
makes consumption from a userland standpoint more difficult.

The real point, though, is that the interface as it currently stands
_does_ allow for this sort of action, and if you want proof, Michael
has actually done a lot of this already in guzzle/streams:

- https://github.com/guzzle/streams/blob/3.0/src/PumpStream.php
(iterable/callable-based streams)
- https://github.com/guzzle/streams/blob/3.0/src/AsyncReadStream.php
(async callable-based stream)

Basically, there are ways to model callables within the streamable
interface, if we get creative -- and they keep the API for interacting
with the body intact.

> 4) My main issue with StreamInterface (or now Streamable, whatever) is that
> it's not wrapping PHP native streams but reimplementing them. Especially
> the stacking/decorating concept is really just reimplementing PHP native
> stream behavior, but slowly. (Without knowing if a Stream is really a
> native stream, even just printing output requires taking the slowest
> approach possible in PHP.) I agree that the PHP native stream API bites,
> but it is the most powerful option we have.
>
> My recommendation at the time was to stop pretending that StreamInterface
> was anything other than a convenience wrapper around real PHP streams, and
> then build its API on that assumption; it ALWAYS contains a stream resource,
> period, and since we know that we can do things like attach a filter or
> whatnot without any extra work. (And that means all existing PHP stream
> wrappers or stream filters are automatically supported. Spiffy!) I still
> believe that is the optimal way forward here.

See the above -- if we assume it's ALWAYS a stream, we cannot also
have callables. However, if we treat it as an _abstraction_ on top of
streams, we have the possibility of representing other paradigms *as
if* they were streams. What that also means is we can use tools like
I've mentioned earlier in this thread, including GuzzleStreamWrapper
(https://github.com/guzzle/streams/blob/master/src/GuzzleStreamWrapper.php)
to add filters, etc. -- even if the actual underlying resource is NOT
a stream. I think that's pretty awesome, actually.

> 5) The current spec does not include a breakdown of the URI, true. However,
> it does support passing or returning a __toString()-able object. That was
> added specifically to support a dedicated object to represent a URI, on
> which we could also have various methods for getting path parameters, etc.
> There was some time ago a proposal for a URI interface. It was quite good I
> thought but the submitter never kept up with it. I believe that, rather
> than adding more methods to the MessageInterface, is the best way to handle
> the URI portion of the message. It's complex enough to warrant its own
> dedicated interface/object. Whether we want to fold that into PSR-7 is
> debatable, though. (Realistically it is an interface with only one
> meaningful implementation.)

I agree -- this should be a separate proposal -- and, to my
understanding of the interfaces, this was the exact reason. For the
purposes of the message interfaces, we just need something that can be
_represented_ as a string. Ideally, that thing is an object, giving us
OO access to things like the path, host, etc. (In
https://github.com/phly/http I created an implementation like this,
and the request object will cast any string set for the URI to an
object.) My feeling is that if FIG provides an implementation of the
HTTP message interfaces, it should do an OOP URI instance as well --
and if a URI proposal is later ratified, update it to follow the
proposal (if the proposal doesn't already mirror it).

> 6) On the subject of mutability, I spoke out in favor of immutable objects
> previously. However, for middlewares I can see where the objects must be
> mutable. That, however, leads to the next question which is where those
> objects store data being passed between middlewares. In Symfony, there's
> the $request->attributes public bag property. I quite hate it, to be
> honest, but I don't know of a good alternative. That may be out of scope at
> the moment but is something we should be keeping in mind if encouraging a
> middleware ecosystem is part of the goal.

This is an interesting conundrum on the server-side, actually. In Ruby
and JS, where middleware is being used extensively already, the
request object is typically either an anonymous hash or an object --
and as such, in those languages, arbitrary properties can be added
on-the-fly. This gives enormous flexibility to middleware, as it means
that the request instance becomes the messenger of application state.

The result is that you can have middleware that parses the query
string parameters and injects them as a property into the request. Or
that parses the body parameters and injects them into the request.

Personally, I fell this would alleviate any need to add further
accessors to the request object for things like query string
arguments, body parameters, env variables, etc -- instead, middleware
could be used to add them to the request as needed:

$post = $_POST;
$query = $_GET;
$env = $_ENV;
$app->pipe(function ($req, $res, $next) use ($post, $query, $env) {
$req->body = $post;
$req->query = $query;
$req->env = $env;
$next();
});

In Conduit, I created a decorator around the phly/http Request
implementation that provides the "mutability" via property overloading
(https://github.com/phly/conduit/blob/master/src/Http/Request.php),
and Conduit then decorates any incoming request instance with it in
order to ensure that any middleware invoked can manipulate the request
in this way. I'm unsure if it's something we should build in to the
interfaces, or if it should be left an implementation detail.
Considering that it would allow us to NOT add in the accessors for
query, body, env, etc., I'm leaning towards _recommending_ (as in
SHOULD language in the spec) that implementations provide property
overloading for this purpose.

Does anybody else want to chime in on this aspect?

--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Paul M. Jones

unread,
Sep 30, 2014, 11:19:42 AM9/30/14
to php...@googlegroups.com

On Sep 30, 2014, at 9:12 AM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:

> The **reason** I want to see this proposal completed and ratified is
> due to my belief that it will have immense server-side ramifications
> in terms of interoperability.

Agreed.


> The proposal is strictly (in its current form) detailing HTTP
> messages. These are useful for **BOTH** server-side code **AND**
> clients. I have validated the former (via
> https://github.com/phly/conduit), while Michael has validated the
> latter (via Guzzle).
>
> My point here is that, as they stand right now, the interfaces
> describe general-purpose HTTP messages, plain and simple, and those
> are used in both sides of an HTTP request.

Yes, I agree that as a general purpose HTTP message description, the proposal as it stands is asymptotically close to complete. But as a system specifically tuned to the needs of a server-side incoming Request, I assert it is *in*sufficient and needs significant work. (I am in favor of us actually doing this work to resolve the insufficiencies.)


> This can be done already. In fact, I've done this when testing Conduit
> itself -- I created request and response objects, and tested what I
> had after executing middleware. It "just works" already.

I may have missed this -- how did you, for example, get the path out of the Request? I recall an additional URI object that is not part of the PSR-7 proposal. In combination with the following ...

> Personally, I fell this would alleviate any need to add further
> accessors to the request object for things like query string
> arguments, body parameters, env variables, etc -- instead, middleware
> could be used to add them to the request as needed:
>
> $post = $_POST;
> $query = $_GET;
> $env = $_ENV;
> $app->pipe(function ($req, $res, $next) use ($post, $query, $env) {
> $req->body = $post;
> $req->query = $query;
> $req->env = $env;
> $next();
> });

... are you suggesting that each implementor of PSR-7 add their own ways of accessing those very commonly used elements? That strikes me as an oversight that will lead to a lot of duplication, and perhaps conflict, in userland. This is not the scenario we purport to address; it means that for Project A to share a Request with Project B, both projects' special elements will need to be added to the Request. Unexpected differences between the elements will lead to various unexpected outcomes.

I stress again that for PSR-7 to be not merely "usable" but also "effective for the most common cases" in a server-side incoming Request situation, we will need more elements, specifically related to very common things like URL discovery, $_GET/POST/FILES, and so on.

Chris Fidao

unread,
Sep 30, 2014, 11:46:53 AM9/30/14
to php...@googlegroups.com
One point of possible interest -

I think the difference between PHP and other languages here is that the other languages "Speak" TCP (and some HTTP), the protocol. They can bind to sockets and listen for TCP connections and then apply the HTTP protocol to manage/handle/fulfill requests.

In PHP, on the other hand, we would be using objects to describe HTTP, but not speak HTTP directly. We still rely on web servers to handle sockets and incoming (TCP or HTTP) requests. (Sidenote: I'm not clear on what/how the built-in PHP web server works, maybe PHP can speak HTTP natively now?)

I don't believe this point lessons the "need" for PSR-7 (A common HTTP messaging interface would be a great boon for framework interoperability), but I think describing this as you did may lead to that confusion. 

If I'm reading this right, there is definitely a difference between the HTTP libraries of other languages and what PSR-7 is doing. 

I'm pretty certain that you're looking more for common interfaces in how code can interact with HTTP, rather than language features and its ability to bind to TCP sockets.

Sound accurate?

Matthew Weier O'Phinney

unread,
Sep 30, 2014, 11:51:13 AM9/30/14
to php...@googlegroups.com
On Tue, Sep 30, 2014 at 10:19 AM, Paul M. Jones <pmjo...@gmail.com> wrote:
>
> On Sep 30, 2014, at 9:12 AM, Matthew Weier O'Phinney <mweiero...@gmail.com> wrote:
>> The proposal is strictly (in its current form) detailing HTTP
>> messages. These are useful for **BOTH** server-side code **AND**
>> clients. I have validated the former (via
>> https://github.com/phly/conduit), while Michael has validated the
>> latter (via Guzzle).
>>
>> My point here is that, as they stand right now, the interfaces
>> describe general-purpose HTTP messages, plain and simple, and those
>> are used in both sides of an HTTP request.
>
> Yes, I agree that as a general purpose HTTP message description, the proposal as it stands is asymptotically close to complete. But as a system specifically tuned to the needs of a server-side incoming Request, I assert it is *in*sufficient and needs significant work. (I am in favor of us actually doing this work to resolve the insufficiencies.)
>
>
>> This can be done already. In fact, I've done this when testing Conduit
>> itself -- I created request and response objects, and tested what I
>> had after executing middleware. It "just works" already.
>
> I may have missed this -- how did you, for example, get the path out of the Request? I recall an additional URI object that is not part of the PSR-7 proposal. In combination with the following ...

I did it via a RequestFactory that pulls data from primarily $_SERVER.
In this particular case:

- https://github.com/phly/http/blob/master/src/RequestFactory.php#L126

The point being that you can write tools that extract that information
from the PHP environment and then push the information into the
Request instance. (I used this approach largely due to conversations
with you, Paul! Makes the code easier to test, and also separates the
PHP-env-specific logic from the implementation.)

As you noted, I ended up creating a URI implementation inside of
phly/http. This allows me to provide easy access to the URI parts
without requiring developers to run the URI through parse_url() to
grab them. Ideally, FIG should likely provide a URI proposal as well,
but I think it's outside the scope of this current proposal -- the
interfaces here indicate either a string, or an object that can be
cast to a string. As long as any future proposal adheres to that
latter, it remains compatible.

>> Personally, I fell this would alleviate any need to add further
>> accessors to the request object for things like query string
>> arguments, body parameters, env variables, etc -- instead, middleware
>> could be used to add them to the request as needed:
>>
>> $post = $_POST;
>> $query = $_GET;
>> $env = $_ENV;
>> $app->pipe(function ($req, $res, $next) use ($post, $query, $env) {
>> $req->body = $post;
>> $req->query = $query;
>> $req->env = $env;
>> $next();
>> });
>
> ... are you suggesting that each implementor of PSR-7 add their own ways of accessing those very commonly used elements? That strikes me as an oversight that will lead to a lot of duplication, and perhaps conflict, in userland. This is not the scenario we purport to address; it means that for Project A to share a Request with Project B, both projects' special elements will need to be added to the Request. Unexpected differences between the elements will lead to various unexpected outcomes.
>
> I stress again that for PSR-7 to be not merely "usable" but also "effective for the most common cases" in a server-side incoming Request situation, we will need more elements, specifically related to very common things like URL discovery, $_GET/POST/FILES, and so on.

This is defintitely one place that middleware can be incompatible
across consumers.

In node, for parsing the query string parameters, you typically end up
with this:

var query = urlparse(req.uri.query); /* where urlparse is a
standard node module */

and middleware that wants to be standalone will do this instead of
accessing any request properties (other than the URI, which is
guaranteed to be present). It means a fair amount of code duplication,
but the operations are ridiculously fast, and as such, make sense to
inline in your code. In PHP, this could be done using either $_GET or,
to remain env-agnostic, using parse_url + parse_str on the request
URI:

$query = parse_str(parse_url($req->getUri(), PHP_URL_QUERY));

(In phly/http, that would become $query = parse_str($req->getUri()->query;)

But since there's no concept of "$_POST" in node, you have two options:

- parse the body yourself (after first determining the incoming
Content-Type, and thus a parser strategy to use)
- rely on other middleware to do it and inject the Request object
(often by wrapping your own middleware in that other middleware)

The above is still true in PHP if you have non form/urlencoded body
content; something, somewhere, needs to determine the content-type and
parse it.

My point is that, at best, we can add accessors for injecting and
retrieving some of these aspects, but we should not be intimating that
an implementation is necessarily responsible for doing that work. If
anything I would argue that we should recommend that utilities be
written to do the parsing/injection OUTSIDE OF the request
implementation.

As such, I'd recommend only the following:

- (get|set)QueryParams()
- (get|set)BodyParams()
- (get|set)IncomingFiles()

Env variables can be retrieved already in PHP using getenv(), and I'm
not convinced they should be included in the request object. I feel
similarly about $_SERVER, particularly as the main reasons to access
$_SERVER for purposes of a request object are to seed it in the first
place; code can ask to be injected with values from $_SERVER elsewhere
if needed.

By default, an implementation should return empty values for these; it
should be up to the application and/or middleware to inject the values
into the request.

Also, I'd argue these should ONLY be defined on the RequestInterface.

So... should I open a PR to add these?

--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Matthew Weier O'Phinney

unread,
Sep 30, 2014, 11:55:56 AM9/30/14
to php...@googlegroups.com
On Tue, Sep 30, 2014 at 10:46 AM, Chris Fidao <fide...@gmail.com> wrote:
> One point of possible interest -
>
> I think the difference between PHP and other languages here is that the
> other languages "Speak" TCP (and some HTTP), the protocol. They can bind to
> sockets and listen for TCP connections and then apply the HTTP protocol to
> manage/handle/fulfill requests.
>
> In PHP, on the other hand, we would be using objects to describe HTTP, but
> not speak HTTP directly.

Correct -- we are creating interfaces that describe value objects --
specifically, those in RFC 2616 (https://tools.ietf.org/html/rfc2616),
and, more specifically, sections 4-6. That RFC is talking about HTTP
messages sent over TCP -- and describes ONLY the messages. This is,
and has been, the primary goal of the proposal.

> If I'm reading this right, there is definitely a difference between the HTTP
> libraries of other languages and what PSR-7 is doing.

Not really. Node and Ruby define these objects, and leave the
transport of the objects to underlying network libraries. As such, the
proposal is mirroring what those languages have already defined.
> --
> 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/CTPRa2XP8po/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/8af2d5f9-5736-4325-a1ca-d38c00875e9d%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.



--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Paul M. Jones

unread,
Sep 30, 2014, 12:11:09 PM9/30/14
to php...@googlegroups.com
Hi Matthew,

(As a side note, I am just now realizing the continued use of Node as the baseline reference. I opine we should be careful about transliterating it *too* closely; my guess is that there are some expectations and common cases in PHP land that may render those transliterations less effective or less useful than hoped.)

(As a secondary side note, I would say not to depend too much on a middleware idea. The concepts here lend themselves to many ways of working, and middleware while useful should not be the concentration.)


>> I stress again that for PSR-7 to be not merely "usable" but also "effective for the most common cases" in a server-side incoming Request situation, we will need more elements, specifically related to very common things like URL discovery, $_GET/POST/FILES, and so on.
>
> This is defintitely one place that middleware can be incompatible
> across consumers.
>
> In node, for parsing the query string parameters, you typically end up
> with this:
>
> var query = urlparse(req.uri.query); /* where urlparse is a
> standard node module */
...
> In PHP, this could be done using either $_GET or,
> to remain env-agnostic, using parse_url + parse_str on the request
> URI:
>
> $query = parse_str(parse_url($req->getUri(), PHP_URL_QUERY));
>
> (In phly/http, that would become $query = parse_str($req->getUri()->query;)

This is exactly my point; re-parsing it every time you need it seems wasteful. Perhaps it is a micro-optimization, but I don't think so. We already have $_GET available to us. Since it is used so commonly, and frequently, it makes sense to me to include some form of this in the proposal so it can be reused in a predictable fashion.


> But since there's no concept of "$_POST" in node, you have two options:
>
> - parse the body yourself (after first determining the incoming
> Content-Type, and thus a parser strategy to use)
> - rely on other middleware to do it and inject the Request object
> (often by wrapping your own middleware in that other middleware)
>
> The above is still true in PHP if you have non form/urlencoded body
> content; something, somewhere, needs to determine the content-type and
> parse it.

Again, this underlines my earlier points: we already *have* $_POST for most cases. I assert that the proposal should make some allowance for it.


> My point is that, at best, we can add accessors for injecting and
> retrieving some of these aspects, but we should not be intimating that
> an implementation is necessarily responsible for doing that work. If
> anything I would argue that we should recommend that utilities be
> written to do the parsing/injection OUTSIDE OF the request
> implementation.
>
> As such, I'd recommend only the following:
>
> - (get|set)QueryParams()
> - (get|set)BodyParams()
> - (get|set)IncomingFiles()

I am in favor of these, with perhaps the replacement of (get|set)BodyParams() with (get|set)PostParams(). Because the Body might be JSON-encoded, XML-encoded, whatever, it strikes me as being application-specific and not PHP-specific, so that parsing the Body according to the incoming Content-Type is not something that has to be done over and over. I also continue to assert we need something that returns at least the parse_url() values of the incoming Request URL. Finally, I assert that we need something that captures the path-info parameters, if any, of the incoming Request, as these are also frequently used.




> Env variables can be retrieved already in PHP using getenv(), and I'm
> not convinced they should be included in the request object.

Agreed.


> I feel similarly about $_SERVER, particularly as the main reasons to access
> $_SERVER for purposes of a request object are to seed it in the first
> place; code can ask to be injected with values from $_SERVER elsewhere
> if needed.

Also agreed; the various parts of $_SEVER that originate in the HTTP request seem to be the only valid candidates for inclusion here.

> Also, I'd argue these should ONLY be defined on the RequestInterface.

Also agreed. In fact, I'd go so far as to say there might be a case for an outgoing client Request, and a separate extension for an incoming server Request that adds these common-case methods. (I am not ideologically attached to this case, although I do wonder how useful those particular methods would be on an outgoing client Request.)

Michael Dowling

unread,
Sep 30, 2014, 12:25:41 PM9/30/14
to php...@googlegroups.com, php...@googlegroups.com
I agree with all your points so far, Matthew, however I believe the methods you're recommending/brainstorming are describing an environment not an HTTP message.

HTTP messages have no "incoming files", web servers do. get/setBodyParams describe a very specific type of body data that I think should only be described in the stream of the body. By moving body concerns into two places (the stream and these getters/setters), we've broken the abstraction. Adding get/setQueryString could leave the request URL in an inconsistent state as well.

Maybe there should be a separate interface for describing web servers. I think describing a "PHP web server" request (as has been recommended so far) would no longer be describing just HTTP messages, but rather an environment.

There's also the possibility of just using the data made available by PHP directly without abstracting it.

Thanks,
Michael
> --
> 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/CAJp_myWWgoNh-oHZ%2BQur9Jr6LJ7pGnOJjmUdygSyuxG7fdwYew%40mail.gmail.com.

Paul M. Jones

unread,
Sep 30, 2014, 12:34:07 PM9/30/14
to php...@googlegroups.com

On Sep 30, 2014, at 11:25 AM, Michael Dowling <mtdo...@gmail.com> wrote:

> I agree with all your points so far, Matthew, however I believe the methods you're recommending/brainstorming are describing an environment not an HTTP message.
...
> Maybe there should be a separate interface for describing web servers. I think describing a "PHP web server" request (as has been recommended so far) would no longer be describing just HTTP messages, but rather an environment.

Yes, this aligns my thought that PSR-7 as proposed is (mostly) sufficient for PHP sending a Request and getting back a Response, but insufficient for working with an incoming Request in the way MWOP (and I!) would like.

Matthew Weier O'Phinney

unread,
Sep 30, 2014, 4:04:32 PM9/30/14
to php...@googlegroups.com
On Tue, Sep 30, 2014 at 11:11 AM, Paul M. Jones <pmjo...@gmail.com> wrote:
> (As a side note, I am just now realizing the continued use of Node as the baseline reference. I opine we should be careful about transliterating it *too* closely; my guess is that there are some expectations and common cases in PHP land that may render those transliterations less effective or less useful than hoped.)

Yes and no. They do an excellent job of having a basically 1:1 model
between RFC 2616 and the objects they expose, which, to my mind, is a
big part of why they've been successful. That said, they also started
with a clean slate -- and thus without the baggage of PHP's
superglobals (I'm thinking specifically $_GET and $_POST here).

Why do I use the word "baggage"? Because:

- $_GET has nothing to do with GET requests. It represents the query
string variables -- which can be present in ANY HTTP method invoked!
- $_POST is an anachronism at this point. It has a very, very specific
purpose in PHP, which is to expose application/x-www-form-urlencoded
data sent specifically via POST. In today's world where your data is
very likely coming from a javascript XHR request or via an API call,
you may not be using POST; you're likely not even using form encoded
data.

So, while they're useful for getting up and running, I can also see
them causing confusion, and, in the latter case, largely irrelevant
for modern applications. (I'm not saying _completely_ irrelevant).

That said: they're available everywhere, due to being superglobals,
making them well known and easy to write to. My point, however, is
that they are NOT part of the HTTP message specification in the least,
and I question whether the names make any sense when considering the
actual parts of an HTTP message.

> (As a secondary side note, I would say not to depend too much on a middleware idea. The concepts here lend themselves to many ways of working, and middleware while useful should not be the concentration.)

This is a good point as well -- it's easy to consider middleware, but
we really need to consider the _existing_ ecosystem. And as such, yes,
having a way to access certain incoming data makes sense.

>>> I stress again that for PSR-7 to be not merely "usable" but also "effective for the most common cases" in a server-side incoming Request situation, we will need more elements, specifically related to very common things like URL discovery, $_GET/POST/FILES, and so on.
>>
>> This is defintitely one place that middleware can be incompatible
>> across consumers.
>>
>> In node, for parsing the query string parameters, you typically end up
>> with this:
>>
>> var query = urlparse(req.uri.query); /* where urlparse is a
>> standard node module */
> ...
>> In PHP, this could be done using either $_GET or,
>> to remain env-agnostic, using parse_url + parse_str on the request
>> URI:
>>
>> $query = parse_str(parse_url($req->getUri(), PHP_URL_QUERY));
>>
>> (In phly/http, that would become $query = parse_str($req->getUri()->query;)
>
> This is exactly my point; re-parsing it every time you need it seems wasteful. Perhaps it is a micro-optimization, but I don't think so. We already have $_GET available to us. Since it is used so commonly, and frequently, it makes sense to me to include some form of this in the proposal so it can be reused in a predictable fashion.

I'm in agreement. More below.

>> But since there's no concept of "$_POST" in node, you have two options:
>>
>> - parse the body yourself (after first determining the incoming
>> Content-Type, and thus a parser strategy to use)
>> - rely on other middleware to do it and inject the Request object
>> (often by wrapping your own middleware in that other middleware)
>>
>> The above is still true in PHP if you have non form/urlencoded body
>> content; something, somewhere, needs to determine the content-type and
>> parse it.
>
> Again, this underlines my earlier points: we already *have* $_POST for most cases. I assert that the proposal should make some allowance for it.
>
>
>> My point is that, at best, we can add accessors for injecting and
>> retrieving some of these aspects, but we should not be intimating that
>> an implementation is necessarily responsible for doing that work. If
>> anything I would argue that we should recommend that utilities be
>> written to do the parsing/injection OUTSIDE OF the request
>> implementation.
>>
>> As such, I'd recommend only the following:
>>
>> - (get|set)QueryParams()
>> - (get|set)BodyParams()
>> - (get|set)IncomingFiles()
>
> I am in favor of these, with perhaps the replacement of (get|set)BodyParams() with (get|set)PostParams(). Because the Body might be JSON-encoded, XML-encoded, whatever, it strikes me as being application-specific and not PHP-specific, so that parsing the Body according to the incoming Content-Type is not something that has to be done over and over.

That's actually why I proposed "body params" over "post params" --
that and the discussion earlier in this email. POST is an anachronism,
and totally unrelated to what it actually represents -- it's the
_parameters_ discovered/parsed from the incoming _body_ of the
message. These should be in the method name, not "post".

> I also continue to assert we need something that returns at least the parse_url() values of the incoming Request URL.

That sounds to me like a URI interface. If that's the case, I can whip
one up pretty quickly. It just needs:

- getScheme()
- getHost()
- getPort()
- getPath()
- getQueryString() or getQuery()

and possibly "getUsername()"/"getPassword()" to accommodate HTTP
authentication, though since PHP already has ways to get at that
information, and it's less generally useful, I don't think these are
necessary.

(Personally, I prefer just using object properties for this rather
than explicit getters, but in terms of interfaces, getters make more
sense.)

> Finally, I assert that we need something that captures the path-info parameters, if any, of the incoming Request, as these are also frequently used.

I'm torn on this. Those are a product of routing. A good router
returns a value object with that information -- and how that object
exposes the information will differ -- both in terms of how to get at
the "matched parameters" as well as additional methods (such as the
match identifier/name, what string was matched, etc.). As a result,
I'm not sure if we can expose anything meaningful or generic in the
request instance, unless we have a generic "getRouteMatches()" that
can return something arbitrary. (And arbitrary is the antithesis of an
interface.)


>> Env variables can be retrieved already in PHP using getenv(), and I'm
>> not convinced they should be included in the request object.
>
> Agreed.
>
>
>> I feel similarly about $_SERVER, particularly as the main reasons to access
>> $_SERVER for purposes of a request object are to seed it in the first
>> place; code can ask to be injected with values from $_SERVER elsewhere
>> if needed.
>
> Also agreed; the various parts of $_SEVER that originate in the HTTP request seem to be the only valid candidates for inclusion here.
>
>> Also, I'd argue these should ONLY be defined on the RequestInterface.
>
> Also agreed. In fact, I'd go so far as to say there might be a case for an outgoing client Request, and a separate extension for an incoming server Request that adds these common-case methods. (I am not ideologically attached to this case, although I do wonder how useful those particular methods would be on an outgoing client Request.)

Okay, another cue from Node here (ha!): Node actually differentiates
between a request (used by clients) and an "IncomingRequest" (used by
servers). As such, perhaps we could have an interface for
"IncomingRequest" that extends the RequestInterface to add the above
accessors?

Matthew Weier O'Phinney

unread,
Sep 30, 2014, 4:09:25 PM9/30/14
to php...@googlegroups.com
On Tue, Sep 30, 2014 at 11:25 AM, Michael Dowling <mtdo...@gmail.com> wrote:
> I agree with all your points so far, Matthew, however I believe the methods you're recommending/brainstorming are describing an environment not an HTTP message.

Agreed; see my most recent response to Paul -- I'm thinking we may
want to introduce a new interface, "IncomingRequest", that extends the
base request interface and adds these.

> HTTP messages have no "incoming files", web servers do. get/setBodyParams describe a very specific type of body data that I think should only be described in the stream of the body. By moving body concerns into two places (the stream and these getters/setters), we've broken the abstraction. Adding get/setQueryString could leave the request URL in an inconsistent state as well.

I'm actually wondering if the interface should describe setters at all
for these. Considering that the incoming request interface is
primarily geared at consumption, I could envision a decorator (or
decorators, plural) to a base request that exposes this information
without the need for setters; the information would either be pushed
in via constructor arguments or parsed from the request instance
itself.

> Maybe there should be a separate interface for describing web servers. I think describing a "PHP web server" request (as has been recommended so far) would no longer be describing just HTTP messages, but rather an environment.

Factories would be able to seed the request quite easily; this is the
approach I took with phly/http. That kept the base request
implementation relatively slim and more like a value object, and the
factory did the work of marshaling input from the environment in order
to populate the request.

> There's also the possibility of just using the data made available by PHP directly without abstracting it.

True, but that makes testing more difficult. :)
> 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/CTPRa2XP8po/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/51405A57-17F7-4E53-8157-8C5A95365AB8%40gmail.com.
> For more options, visit https://groups.google.com/d/optout.



Matthew Weier O'Phinney

unread,
Sep 30, 2014, 6:30:46 PM9/30/14
to php...@googlegroups.com
On Tue, Sep 30, 2014 at 3:04 PM, Matthew Weier O'Phinney
<mweiero...@gmail.com> wrote:
> On Tue, Sep 30, 2014 at 11:11 AM, Paul M. Jones <pmjo...@gmail.com> wrote:
>>> My point is that, at best, we can add accessors for injecting and
>>> retrieving some of these aspects, but we should not be intimating that
>>> an implementation is necessarily responsible for doing that work. If
>>> anything I would argue that we should recommend that utilities be
>>> written to do the parsing/injection OUTSIDE OF the request
>>> implementation.
>>>
>>> As such, I'd recommend only the following:
>>>
>>> - (get|set)QueryParams()
>>> - (get|set)BodyParams()
>>> - (get|set)IncomingFiles()

I've created a pull request for this here:

- https://github.com/php-fig/fig-standards/pull/338

>> I also continue to assert we need something that returns at least the parse_url() values of the incoming Request URL.
>
> That sounds to me like a URI interface. If that's the case, I can whip
> one up pretty quickly. It just needs:
>
> - getScheme()
> - getHost()
> - getPort()
> - getPath()
> - getQueryString() or getQuery()
>
> and possibly "getUsername()"/"getPassword()" to accommodate HTTP
> authentication, though since PHP already has ways to get at that
> information, and it's less generally useful, I don't think these are
> necessary.

I've create a pull request for this here:

- https://github.com/php-fig/fig-standards/pull/337
Reply all
Reply to author
Forward
0 new messages