HTTP message package

1,289 views
Skip to first unread message

Chris Wilkinson

unread,
Dec 29, 2012, 5:45:40 PM12/29/12
to php...@googlegroups.com
Hi,

There was a proposal to create a HTTP client interface earlier this year. As was noted at the time, the core HTTP message representation part (ie the request/response objects) is probably of more importance: it's used by both HTTP clients and frameworks.

HTTP clients issue requests (through cURL for example) and return the response to the user. Some frameworks (eg Symfony) convert superglobals into a request object and let the user return a response object (which is then returned to the browser).

It is actually possible to connect the two (eg for functional testing). Disparate objects, however, create problems (for example, the Symfony BrowserKit and HttpKernel components have their own Response classes - see https://github.com/symfony/symfony/issues/4475).

Because of this, there should be a standard RequestInterface and ResponseInterface. The ClientInterface can then be developed separately, obviously making use of them.

For reference, a non-exhaustive list of existing request/response object implementations:

Symfony2 HttpFoundation

Symfony2 BrowserKit

Guzzle

Zend2

Buzz

Requests

Aura

CakePHP

I'd like to propose a Psr\Http package containing something along the lines of:

interface MessageInterface
{
    public function __toString();
    public function getStartLine();
    public function getProtocolVersion();
    public function setProtocolVersion($protocolVersion);
    public function getHeader($header);
    public function getHeaders();
    public function hasHeader($header);
    public function setHeader($header, $value);
    public function setHeaders(array $headers);
    public function addHeaders(array $headers);
    public function getBody();
    public function setBody($body);
}

interface RequestInterface extends MessageInterface
{
    public function __construct($method, $url, $protocolVersion = '1.1');
    public function getResponse();
    public function setResponse(ResponseInterface $response);
    public function getMethod();
    public function setMethod($method);
    public function getUrl();
    public function setUrl($url);
}

interface ResponseInterface extends MessageInterface
{
    public function __construct($statusCode, $reasonPhrase = null, $protocolVersion = '1.1');
    public function getRequest();
    public function setRequest(RequestInterface $request);
    public function getStatusCode();
    public function setStatusCode($statusCode);
    public function getReasonPhrase();
    public function setReasonPhrase($reasonPhrase);
}

This provides the basics for creating and interrogating request/response objects manually, and producing a HTTP message by casting it to a string.

The headers aren't that easy to use though, as you have to set the entire value at once (and seeing what's been set is hard). Most existing implementations provide helper methods for some (if not most) of the headers as a result.

So instead of:

$response->setHeader('Cache-Control', 'private, max-age=3600, must-revalidate');

You could do:

$response->getCacheControlHeader()->setPrivate()->setMaxAge(3600)->setMustRevalidate();

or even just:

$response->setPrivate()->setMaxAge(3600)->setMustRevalidate();

If these are added to the interfaces somehow, they should cover all standard headers (extensions can still be set manually). This does dramatically increase the complexity of implementation though (although you could just extend a provided abstract class, I can't really think of any need for variation), and has possibly extended the remit.

Any thoughts?

Chris (aka thewilkybarkid)

Patrick Mulvey

unread,
Dec 30, 2012, 9:14:13 AM12/30/12
to php...@googlegroups.com
What's the difference between setHeaders() and addHeaders()?

Chris Wilkinson

unread,
Dec 30, 2012, 10:03:16 AM12/30/12
to php...@googlegroups.com
addHeaders() would add to the set of headers (replacing those which already exist), while setHeaders() would replace the whole lot. I excluded PHPDoc to save space, I'll try and get it on GitHub soon. (The syntax is by no means a concrete proposal yet.)

Chris

Patrick Mulvey

unread,
Dec 30, 2012, 10:10:18 AM12/30/12
to php...@googlegroups.com
Hmm. Rather than having the two setter methods, I think it'd be less ambiguous to have a clearHeaders() method to remove existing headers. Then you could do setHeaders() to add new headers and replace existing ones, and clearHeaders()->setHeaders() to replace all of them. As it stands I think it will cause confusion.

Sebastian Krebs

unread,
Dec 30, 2012, 10:22:02 AM12/30/12
to php...@googlegroups.com
Both are widely used patterns for naming methods, so I don't think it will confuse many. Why do you think this?


2012/12/30 Patrick Mulvey <pdmu...@gmail.com>
--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msg/php-fig/-/V0tlGy9XPLMJ.

For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
github.com/KingCrunch

Florin Patan

unread,
Dec 30, 2012, 10:47:08 AM12/30/12
to php...@googlegroups.com
I think you should get a PR up and running against at GitHub and have all the interfaces properly documented. This would help the review process for the proposal as well as help you understand it from a user perspective imo.
Also there's a missing function to allow for appending content to the body of the message as well as providing a stream as a response.
As for providing the helper methods for generic headers you've mentioned, I think the official RFCs should be studied first then we should add them. In your example, based on my understanding of the 'Cache-Control' header, the method should look like this:
$response->setCacheControl(CacheControl::private, 3600, true)
or
$response->setCacheControl(CacheControl::noCache)

And yes, it's confusing to have addHeaders/setHeaders methods without description.

Best regards.

Patrick Mulvey

unread,
Dec 30, 2012, 11:46:04 AM12/30/12
to php...@googlegroups.com
Can't say I've ever noticed this being a common naming convention, but maybe it's just me. 

Michael Dowling

unread,
Dec 30, 2012, 1:00:10 PM12/30/12
to php...@googlegroups.com
It might be useful to go over the previous conversation around an HTTP
proposal and work some of that discussion into your interfaces. We
talked about it a lot. Part of the reason that conversation died off
is because creating a well made HTTP client is really complicated.

Interfaces should not contain __constructors.

I don't think `getStartLine()` belongs on the MessageInterface? Aren't
the HTTP method, path, query string, protocol, status code, etc more
valuable on their own without having to be parsed? A user can get this
information from the first line of the __toString() method if needed.

Regarding addHeader/addHeaders:

I don't feel these methods are ambiguous. In Guzzle, the addHeader()
and addHeaders() methods will add new headers if they are not already
present on a message or append to an existing header if the header was
already present.

> Also there's a missing function to allow for appending content to the body of the message as well as providing a stream as a response.

I think the body should be passed to a message and treated as an
immutable value. There should only be methods on a message to set or
retrieve a body; I don't think there should be methods on a message to
modify the body. That concern should be outside of a message object--
for example, Guzzle uses an EntityBodyInterface object to manage
message bodies (http://guzzlephp.org/api/class-Guzzle.Http.EntityBodyInterface.html).

-Michael
> https://groups.google.com/d/msg/php-fig/-/rSkTUbjsgiUJ.

Larry Garfield

unread,
Dec 30, 2012, 3:39:54 PM12/30/12
to php...@googlegroups.com
First off, definitely +1 to splitting off Request/Response as their own
PSR separate from clients. That's a discrete chunk that's useful all on
its own, and lets us divide and conquer the problem space. (And perhaps
render a complete client API redundant, as the client is just "something
that accepts a PSR request object"?)

On 12/30/2012 12:00 PM, Michael Dowling wrote:
> I don't think `getStartLine()` belongs on the MessageInterface? Aren't
> the HTTP method, path, query string, protocol, status code, etc more
> valuable on their own without having to be parsed? A user can get this
> information from the first line of the __toString() method if needed.

I agree. This information should be parsed out. I can't really think
of when I'd want that line as a string rather than parsed values.

> Regarding addHeader/addHeaders:
>
> I don't feel these methods are ambiguous. In Guzzle, the addHeader()
> and addHeaders() methods will add new headers if they are not already
> present on a message or append to an existing header if the header was
> already present.

Yes, but add and set are very similar operations, and without
documentation it can be rather unclear what the difference is. We
should try to keep the API itself as self-documenting as possible (and
then document the hell out of it anyway).

>
>> Also there's a missing function to allow for appending content to the body of the message as well as providing a stream as a response.
> I think the body should be passed to a message and treated as an
> immutable value. There should only be methods on a message to set or
> retrieve a body; I don't think there should be methods on a message to
> modify the body. That concern should be outside of a message object--
> for example, Guzzle uses an EntityBodyInterface object to manage
> message bodies (http://guzzlephp.org/api/class-Guzzle.Http.EntityBodyInterface.html).

I would agree with not having a modify-body operation other than
get/set. However, I do think that support for streaming responses is
important in many situations so we should try to include it if possible
(or maybe as an extended interface if people prefer). Passing in a
callable or, in PHP 5.5, a generator (that could be fun) is the best way
to handle large returns without blowing out your memory, so let's make
sure that's supported.

I'm also not convinced that having the response code and response code
description separate is a good idea. Those are per spec tightly bound,
so I cannot think of any case where you'd want to send back "HTTP 200
And All's Well".

--Larry Garfield

Patrick Mulvey

unread,
Dec 30, 2012, 4:06:18 PM12/30/12
to php...@googlegroups.com


On Sunday, December 30, 2012 6:00:10 PM UTC, Michael Dowling wrote:

Regarding addHeader/addHeaders:

I don't feel these methods are ambiguous. In Guzzle, the addHeader()
and addHeaders() methods will add new headers if they are not already
present on a message or append to an existing header if the header was
already present.

Fair enough. It's worth noting that this proposal only has addHeaders(), without a singular addHeader() method.

Michael Dowling

unread,
Dec 30, 2012, 5:24:40 PM12/30/12
to php...@googlegroups.com
> I would agree with not having a modify-body operation other than get/set. However, I do think that support for streaming responses is important in many situations so we should try to include it if possible (or maybe as an extended interface if people prefer). Passing in a callable or, in PHP 5.5, a generator (that could be fun) is the best way to handle large returns without blowing out your memory, so let's make sure that's supported.

Yes, support for streaming is very important. That's why I mentioned
Guzzle passes this responsibility off to an EntityBody object to
encapsulate the logic of streaming data onto or off of a data source.
An HTTP PSR could either do something similar (e.g. use an EntityBody
object) or account for various data types passed into the setBody()
method.

> I'm also not convinced that having the response code and response code description separate is a good idea. Those are per spec tightly bound, so I cannot think of any case where you'd want to send back "HTTP 200 And All's Well".

These should be separated. The HTTP spec states that the reason
phrases listed in the spec are recommendations and can be freely
modified. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html --
"The reason phrases listed here are only recommendations -- they MAY
be replaced by local equivalents without affecting the protocol."

-Michael Dowling
> https://groups.google.com/d/msg/php-fig/-/o8L-SJLbmewJ.

Beau Simensen

unread,
Dec 31, 2012, 11:07:09 AM12/31/12
to php...@googlegroups.com
Super excited to see this discussion picked up again! If defining the HTTP Messages prior to defining a client makes sense I'm all for it.



I like the idea of the body being get and set as an object. Unclear on whether or not it makes sense for it to be optionally a string or a callable or something. But flexibility beyond "just a string" would be a good idea.



How important is it that the Request and Response know about each other? I sorta feel like they really don't *need* to know about each other but maybe I'm missing some common use cases there? If they don't *need* to know about each other I think I'd prefer to keep the interfaces simpler and drop them.

My expected usage would be `$response = $whatever->makeRequest($response)` ... I know that I've used clients that went more like, $request = $whatever->makeRequest('GET', 'http:/...'); $response = $request->getResponse()` but I think that those are implementation details that are out of scope for the pure message interface.



Interfaces should not contain __constructors. 

I agree with this.

In lieu of constructors in the Request interface I'd propose a Request factory interface be created.

interface RequestFactoryInterface
{
    /**
     * @return RequestInterface
     */
    public function createRequest($method, $url, $protocolVersion = '1.1');
}

This would allow for the creation of Requests to be swappable.

Are there use cases for end-users creating Response objects? That seems like the responsibility of a client provided by a vendor and since each vendor is going to probably have highly specialized handling in the creation of response objects I'm not sure what utility there is in defining a Response factory. Maybe it would make sense for completeness so I would not be opposed to it.



Would it make sense to split the write part of the interfaces out into another interface?

The reason being that I have an expectation when I pass something like a request object to a client the client won't actually make changes to the request itself. Similarly, when I get a response object from a client, I don't have any reasonable expectation that I'll be making changes to the response.

Request: User creates and can update, client only receives a read-only object
Response: Client creates and can update, user only receives a read-only object

Example:

interface MessageInterface
{
    public function getStartLine();
    public function getProtocolVersion();
    public function getHeader($header);
    public function getHeaders();
    public function hasHeader($header);
    public function getBody();
}

interface WritableMessageInterface extends MessageInterface
{
    public function setProtocolVersion($protocolVersion);
    public function setHeader($header, $value);
    public function setHeaders(array $headers);
    public function addHeaders(array $headers);
    public function setBody($body);
}

interface RequestInterface extends MessageInterface
{
    public function getMethod();
    public function getUrl();
}

interface WriteableRequestInterface extends RequestInterface implements WritableMessageInterface
{
    public function setMethod($method);
    public function setUrl($url);
}

interface ResponseInterface extends MessageInterface
{
    public function getStatusCode();
    public function getReasonPhrase();
}

interface WritableResponseInterface extends ResponseInterface implements WritableMessageInterface
{
    public function setStatusCode($statusCode);
    public function setReasonPhrase($reasonPhrase);
}


A hypothetical PSR HTTP Client would only deal in read-only messages in and out:

interface Client
{
    /**
     * @return ResponseInterface
     */
    public function makeRequest(RequestInterface $request);
}


This would reduce the overall complexity of the client and user to the following:

interface EffectiveRequestInterface
{
    public function getStartLine();
    public function getProtocolVersion();
    public function getHeader($header);
    public function getHeaders();
    public function hasHeader($header);
    public function getBody();
    public function getMethod();
    public function getUrl();
}

interface EffectiveResponseInterface
{
    public function getStartLine();
    public function getProtocolVersion();
    public function getHeader($header);
    public function getHeaders();
    public function hasHeader($header);
    public function getBody();
    public function getStatusCode();
    public function getReasonPhrase();
}


If we went with using a request factory interface, we would change the return value of the createRequest method to return a writable request interface. This would ensure that the end-user could easily get a Request object they can modify hopefully mitigating some of the overall complexity of having read/write interfaces to choose from.


interface RequestFactoryInterface
{
    /**
     * @return WritableRequestInterface
     */
    public function createRequest($method, $url, $protocolVersion = '1.1');
}

Chris Wilkinson

unread,
Dec 31, 2012, 12:09:06 PM12/31/12
to php...@googlegroups.com
I've just created a PR at https://github.com/php-fig/fig-standards/pull/72.

On Monday, 31 December 2012 16:07:09 UTC, Beau Simensen wrote:
How important is it that the Request and Response know about each other? I sorta feel like they really don't *need* to know about each other but maybe I'm missing some common use cases there? If they don't *need* to know about each other I think I'd prefer to keep the interfaces simpler and drop them.

I think it's more likely that you want to know the response for a request rather than a request for a response, I made it bi-directional for completeness. Whether they should be there at all I'm not sure.

Are there use cases for end-users creating Response objects? That seems like the responsibility of a client provided by a vendor and since each vendor is going to probably have highly specialized handling in the creation of response objects I'm not sure what utility there is in defining a Response factory. Maybe it would make sense for completeness so I would not be opposed to it.

When using them in frameworks rather than HTTP clients the user could create the response manually, setting the headers directly for example (or it's created for them as a result of annotations etc).

Factories could be useful for creating a Request object from the superglobals (in frameworks) or Response objects from a HTTP message (in clients), but that might be an implementation detail.

Chris 

Patrick Mulvey

unread,
Dec 31, 2012, 12:13:21 PM12/31/12
to php...@googlegroups.com


On Monday, December 31, 2012 4:07:09 PM UTC, Beau Simensen wrote:

Interfaces should not contain __constructors. 

I agree with this.


Why? What's the rationale here?

Marco Pivetta

unread,
Dec 31, 2012, 12:22:30 PM12/31/12
to php...@googlegroups.com
Interfaces don't define how you have to initialize (or cleanup, in case of destructors) an object. They just define its expected behavior of it once it's already initialized. There's enough docs on it on the web. It's even disallowed to have constructors in interfaces in some OO languages.

@Chris I hope my comments on the PR make some sense to you
--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msg/php-fig/-/VMA1f5_tGG8J.

Patrick Mulvey

unread,
Dec 31, 2012, 12:32:58 PM12/31/12
to php...@googlegroups.com


On Monday, December 31, 2012 5:22:30 PM UTC, Marco Pivetta wrote:
Interfaces don't define how you have to initialize (or cleanup, in case of destructors) an object. They just define its expected behavior of it once it's already initialized. There's enough docs on it on the web. It's even disallowed to have constructors in interfaces in some OO languages.


But PHP does allow it. What's the downside of defining constructors in interfaces? How does it actually harm anything? I've Googled it but didn't find anything particularly useful. 

Evert Pot

unread,
Dec 31, 2012, 12:33:03 PM12/31/12
to php...@googlegroups.com
On Dec 31, 2012, at 6:22 PM, Marco Pivetta <ocra...@gmail.com> wrote:

> Interfaces don't define how you have to initialize (or cleanup, in case of destructors) an object. They just define its expected behavior of it once it's already initialized. There's enough docs on it on the web. It's even disallowed to have constructors in interfaces in some OO languages.

Also, in PHP the constructor signature is completely ignored for inheritance. You can add additional arguments to a subclass or implementation without issue; likely for the same reasons.

Evert

Marco Pivetta

unread,
Dec 31, 2012, 12:37:41 PM12/31/12
to php...@googlegroups.com
Anything IOC related becomes really hard when you got an interface that defines a constructor. Constructors vary a lot depending on implementations, and usually are not really related with how the object behaves once instantiated.

Can we cut this part of the discussion on this particular thread? It's very OT :)

Patrick Mulvey

unread,
Dec 31, 2012, 12:47:33 PM12/31/12
to php...@googlegroups.com


On Monday, December 31, 2012 5:37:41 PM UTC, Marco Pivetta wrote:
Anything IOC related becomes really hard when you got an interface that defines a constructor. Constructors vary a lot depending on implementations, and usually are not really related with how the object behaves once instantiated.

Can we cut this part of the discussion on this particular thread? It's very OT :)

Sure. Sorry.

Michael Dowling

unread,
Dec 31, 2012, 7:58:50 PM12/31/12
to php...@googlegroups.com
1. How will multi-valued headers be added, retrieved, etc? Some people
think every call to `getHeader()` should return an array. Guzzle uses
a Header class to store headers so that they are case-insensitive, can
be used as strings, and can be iterated like an array:
https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Message/Header.php

2. How will transactional history of an HTTP request/response be
handled (e.g. redirects and requests issued to return a response)?
People previously recommended implementing a `getPreviousResponse()`
method on a response class. Guzzle uses this implementation and it
allows you to see every request/response issued that led to a
particular response. Maybe this is outside the scope of this PSR.

3. I'd either like to see a URL class be defined or to add the
following methods to a request: get/setQuery() (would return a query
string array/object/etc), get/setPath(), get/setHost(), get/setPort(),
get/setScheme() (e.g. http, https, etc), get/setPassword(),
get/setUsername(), etc. This is very helpful for both client and
server applications that would use this PSR.

4. How will query strings be represented? Should everything just be
passed into the request's URL as a string, or should we have
functionality to provide a more associative array type functionality?
One thing to keep in mind is that different servers use different
representations of multi-valued query string variables. Guzzle uses a
QueryString object to handle this:
https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/QueryString.php

Simensen: I think the idea of creating mutable vs immutable requests
is really complicated and would make it hard to add functionality to
an HTTP client implemented with this proposed interface. I also think
clients, observers, or whatever else people choose to implement on top
of these PSRs should be able to modify a request or response as needed
(take a look at Guzzle's plugin system for examples).

I probably will have a whole slew of other questions about this PSR,
but it's New Year's Eve so I gotta run for now.

-Michael
> --
> You received this message because you are subscribed to the Google Groups
> "PHP Framework Interoperability Group" group.
> To post to this group, send email to php...@googlegroups.com.
> To unsubscribe from this group, send email to
> php-fig+u...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/php-fig/-/xjsJLxbhJOEJ.

Paul Jones

unread,
Dec 31, 2012, 8:48:24 PM12/31/12
to php...@googlegroups.com

On Dec 31, 2012, at 6:58 PM, Michael Dowling wrote:

> 1. How will multi-valued headers be added, retrieved, etc? Some people
> think every call to `getHeader()` should return an array. Guzzle uses
> a Header class to store headers so that they are case-insensitive, can
> be used as strings, and can be iterated like an array:
> https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Message/Header.php

Aura does something similar as well. I think a useful addition here would be a "Headers" collection class and perhaps an individual "Header" class to represent the header itself. (The idea here is that you can have one header field repeated multiple times; a straight-up key-value array of "field => value" is temptingly straightforward but ends up being not enough.)


> 2. How will transactional history of an HTTP request/response be
> handled (e.g. redirects and requests issued to return a response)?
> People previously recommended implementing a `getPreviousResponse()`
> method on a response class. Guzzle uses this implementation and it
> allows you to see every request/response issued that led to a
> particular response. Maybe this is outside the scope of this PSR.

I think this is outside the scope of this PSR. I find it easy to imagine a project building its own stack of requests and responses to track this kind of thing.


> 3. I'd either like to see a URL class be defined or to add the
> following methods to a request: get/setQuery() (would return a query
> string array/object/etc), get/setPath(), get/setHost(), get/setPort(),
> get/setScheme() (e.g. http, https, etc), get/setPassword(),
> get/setUsername(), etc. This is very helpful for both client and
> server applications that would use this PSR.

I agree that a URL object would be useful, but I also think it's complex enough to be outside the scope of this PSR. Projects can use their own URL building tools, cast to string, and pass that string to the message.


> 4. How will query strings be represented? Should everything just be
> passed into the request's URL as a string, or should we have
> functionality to provide a more associative array type functionality?
> One thing to keep in mind is that different servers use different
> representations of multi-valued query string variables. Guzzle uses a
> QueryString object to handle this:
> https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/QueryString.php

Ditto for query strings. I think the URL+query string idea should be their own PSR.


> I think the idea of creating mutable vs immutable requests
> is really complicated and would make it hard to add functionality to
> an HTTP client implemented with this proposed interface.

Agreed. Strip it down as much as we can to *just* the message (whether request or response).


-- pmj

Paul Jones

unread,
Dec 31, 2012, 8:58:01 PM12/31/12
to php...@googlegroups.com

On Dec 31, 2012, at 10:07 AM, Beau Simensen wrote:

> I like the idea of the body being get and set as an object. Unclear on whether or not it makes sense for it to be optionally a string or a callable or something. But flexibility beyond "just a string" would be a good idea.

As do I. I think the spec should state the body param as "mixed" so that it can be a string or an object; clients can be instructed to cast it to a string for sending.


> How important is it that the Request and Response know about each other? I sorta feel like they really don't *need* to know about each other but maybe I'm missing some common use cases there? If they don't *need* to know about each other I think I'd prefer to keep the interfaces simpler and drop them.

I agree here too. There's no need for a request to know about its response, or vice versa. I find it easy to imagine client implementations building their own stacks to track this sort of thing.


> Are there use cases for end-users creating Response objects?

There's at least one. For example, a page controller object might create a Response directly, or pass back a descriptor that a front controller would use to build an HTTP response proper. (This is how Aura works.)


-- pmj

Paul Jones

unread,
Dec 31, 2012, 9:58:24 PM12/31/12
to php...@googlegroups.com
Hi all,

I really like this proposal. I think addressing just the message portion of HTTP is a win all around. I also applaud Chris Wilkinson for actually doing the research and using existing projects as the basis for his work. Nicely done.

Of course, there is no stronger urge than the desire to edit someone else's work. ;-)

With that in mind, I have edited Chris initial proposal based on the subsequent discussion and and some other ideas; it is below. This edit, should Chris think it worthwhile, makes the following changes:

1. Extracts get/set/addHeaders() to a hypothetical Headers object; the get/setHeaders() methods would work with this object, which would control adding/setting/getting headers. Likely a Header object would also be needed.

2. In my (small "o") opinion, cookies are different and special enough to warrant their own treatment; this is represented by a hypothetical CookiesInterface with get/setCookies() methods.

3. The get/set Response/Request have been removed.

What do we think?

* * *
<?php
/**
* Psr\Http\MessageInterface
*/
namespace Psr\Http;

use Psr\Http\Exception\InvalidArgumentException;
use Psr\Http\HeadersInterface;
use Psr\Http\CookiesInterface;

/**
*
* HTTP messages consist of requests from client to server and responses from
* server to client.
*
* This interface is not to be implemented directly, instead implement
* `RequestInterface` or `ResponseInterface` as appropriate.
*
*/
interface MessageInterface
{
/**
*
* Returns the message as an HTTP string.
*
* @return string Message as an HTTP string.
*
*/
public function __toString();

/**
*
* Gets the HTTP protocol version.
*
* @return string HTTP protocol version.
*
*/
public function getProtocolVersion();

/**
*
* Sets the HTTP protocol version.
*
* @param string $version The HTTP protocol version.
*
* @return MessageInterface A reference to the message.
*
* @throws InvalidArgumentException When the HTTP protocol version is not valid.
*
*/
public function setProtocolVersion($version);

/**
*
* Sets the collection of all headers.
*
* @return Psr\Http\HeadersInterface
*
*/
public function setHeaders(HeadersInterface $headers);

/**
*
* Gets the collection of all headers.
*
* @return Psr\Http\HeadersInterface
*
*/
public function getHeaders();

/**
*
* Sets the collection of all cookies.
*
* @return Psr\Http\CookiesInterface
*
*/
public function setCookies(CookiesInterface $cookies);

/**
*
* Gets the collection of all cookies.
*
* @return Psr\Http\CookiesInterface
*
*/
public function getCookies();

/**
*
* Gets the body.
*
* This returns the original form, in contrast to `getBodyAsString()`.
*
* @return mixed|null Body, or null if not set.
*
* @see getBodyAsString()
*
*/
public function getBody();

/**
*
* Gets the body as a string.
*
* If the body is a stream, it will be read at this point.
*
* @return string|null Body as a string, or null if not set.
*
*/
public function getBodyAsString();

/**
*
* Sets the body.
*
* The body SHOULD be one of:
*
* - string
* - callable that returns a string
* - object that implements `Serializable`
* - `SimpleXMLElement`
* - stream
* - null
*
* An implementation MUST reject any other form that it does not know how
* to turn into a string.
*
* Anything other than a stream MAY immediately be turned into a string;
* both forms MUST be stored.
*
* An implementation MUST NOT read a stream at this point.
*
* A null value will remove the existing body.
*
* @param mixed $body Body.
*
* @return MessageInterface Reference to the message.
*
* @throws InvalidArgumentException When the body is not valid.
*
*/
public function setBody($body);
}


/**
* Psr\Http\RequestInterface
*/

namespace Psr\Http;

use Psr\Http\Exception\InvalidArgumentException;

/**
* A request message from a client to a server.
*/
interface RequestInterface extends MessageInterface
{
/**
* Sets the method.
*
* @param string $method Method.
*
* @return RequestInterface Reference to the request.
*/
public function setMethod($method);

/**
*
* Gets the method.
*
* @return string Method.
*
*/
public function getMethod();

/**
*
* Sets the absolute URL.
*
* @param string $url URL.
*
* @return RequestInterface Reference to the request.
*
* @throws InvalidArgumentException If the URL is invalid.
*
*/
public function setUrl($url);

/**
*
* Gets the absolute URL.
*
* @return string URL.
*
*/
public function getUrl();
}

/**
* Psr\Http\ResponseInterface
*/
namespace Psr\Http;

use Psr\Http\Exception\InvalidArgumentException;

/**
* A request message from a server to a client.
*/
interface ResponseInterface extends MessageInterface
{
/**
*
* Sets the response status code.
*
* @param integer $statusCode Status code.
*
* @return self
*
* @throws InvalidArgumentException When the status code is not valid.
*
*/
public function setStatusCode($code);

/**
*
* Gets the response status code.
*
* @return string Status code.
*
*/
public function getStatusCode();

/**
*
* Sets the response reason phrase.
*
* @param string $reasonPhrase Reason phrase.
*
* @return self
*
*/
public function setReasonPhrase($phrase);

/**
*
* Gets the response reason phrase.
*
* If it has not been explicitly set using `setReasonPhrase()` it SHOULD
* return the RFC 2616 recommended reason phrase.
*
* @return string|null Reason phrase, or null if unknown.
*
*/
public function getReasonPhrase();
}

* * *

-- pmj

Ryan McCue

unread,
Jan 1, 2013, 1:14:57 AM1/1/13
to php...@googlegroups.com
Michael Dowling wrote:
> 1. How will multi-valued headers be added, retrieved, etc? Some people
> think every call to `getHeader()` should return an array. Guzzle uses
> a Header class to store headers so that they are case-insensitive, can
> be used as strings, and can be iterated like an array:
> https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Message/Header.php

Likewise for Requests:
https://github.com/rmccue/Requests/blob/master/library/Requests/Response/Headers.php

In response to what Paul noted (multiple headers), RFC2616 notes that
they can be combined with a comma. I'm probably going to create a
separate header class for single headers that will give a smarter
response than a string, but implement __toString() with that rule.

> 2. How will transactional history of an HTTP request/response be
> handled (e.g. redirects and requests issued to return a response)?
> People previously recommended implementing a `getPreviousResponse()`
> method on a response class. Guzzle uses this implementation and it
> allows you to see every request/response issued that led to a
> particular response. Maybe this is outside the scope of this PSR.

Requests uses a $history array property on the response which it builds
while running through the redirects. I think this should be part of the
interface specification, since non-standard behaviour here would be a pain.

> Simensen: I think the idea of creating mutable vs immutable requests
> is really complicated and would make it hard to add functionality to
> an HTTP client implemented with this proposed interface. I also think
> clients, observers, or whatever else people choose to implement on top
> of these PSRs should be able to modify a request or response as needed
> (take a look at Guzzle's plugin system for examples).

Requests uses a similar method to allow for everything from
authentication handlers to asynchronous multiple request support. That
said, that's not really in the scope of this PSR.

--
Ryan McCue
<http://ryanmccue.info/>

Marco Pivetta

unread,
Jan 1, 2013, 11:35:05 AM1/1/13
to php...@googlegroups.com
Answers inline

On 1 January 2013 01:58, Michael Dowling <mtdo...@gmail.com> wrote:
1. How will multi-valued headers be added, retrieved, etc? Some people
think every call to `getHeader()` should return an array. Guzzle uses
a Header class to store headers so that they are case-insensitive, can
be used as strings, and can be iterated like an array:
https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Message/Header.php


I think type-hinting "Traversable|ArrayAccess|array" could be enough, no
 
2. How will transactional history of an HTTP request/response be
handled (e.g. redirects and requests issued to return a response)?
People previously recommended implementing a `getPreviousResponse()`
method on a response class. Guzzle uses this implementation and it
allows you to see every request/response issued that led to a
particular response. Maybe this is outside the scope of this PSR.

Not sure why a response object would be aware of previous response objects.
That would be part of some greater "container" (the client? the server?) and not of the response itself.
A response is a larger data packet to me, and in my opinion it would probably be:

 - serializable
 - aware only of itself
 - a value object

If we assume that a Response object has a reference to a "previous response", then this means that
the Response object has to be passed along with all previous ones. This makes it kind of complicated
to pass the object around. The job of keeping history track is not part of the response itself in my opinion.
There is not even such a concept in HTTP itself AFAIK.

3. I'd either like to see a URL class be defined or to add the
following methods to a request: get/setQuery() (would return a query
string array/object/etc), get/setPath(), get/setHost(), get/setPort(),
get/setScheme() (e.g. http, https, etc), get/setPassword(),
get/setUsername(), etc. This is very helpful for both client and
server applications that would use this PSR.

I am quite ignorant on this, but Zend\Uri\UriInterface may be what you're looking for.

4. How will query strings be represented? Should everything just be
passed into the request's URL as a string, or should we have
functionality to provide a more associative array type functionality?
One thing to keep in mind is that different servers use different
representations of multi-valued query string variables. Guzzle uses a
QueryString object to handle this:
https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/QueryString.php

The query string is part of the URI itself, no? We can use URI strings or the URI interface depending on what
we need with the assumption that internally, all URIs are valid (means that anything being `->setUri` should
throw `InvalidArgumentException`
 
Simensen: I think the idea of creating mutable vs immutable requests
is really complicated and would make it hard to add functionality to
an HTTP client implemented with this proposed interface. I also think
clients, observers, or whatever else people choose to implement on top
of these PSRs should be able to modify a request or response as needed
(take a look at Guzzle's plugin system for examples).

It would be cool to have an immutable request/response, but even in real world requests/responses are modified
by reverse http proxies, web servers and so on during the path to the client...

I probably will have a whole slew of other questions about this PSR,
but it's New Year's Eve so I gotta run for now.

-Michael


I have this weird feeling that many people are using the request object directly to issue a request, and not some http client:

`$request->send();`

I don't think this is correct, since the data packet doesn't send itself. I'd personally keep it small and slick :)

Chris Wilkinson

unread,
Jan 2, 2013, 5:06:59 AM1/2/13
to php...@googlegroups.com
I've just made a few changes to the PR, including removing the link between requests/responses (at least for the moment).

Re headers, I think there are 3 options. 

(The below isn't defining syntax, merely the styles of doing it.)

1) The bare minimum (what's there at the moment).

This has only a few methods which involve setting string values (and arrays that are then imploded).

$message->setHeader('Cache-Control', 'private, max-age=3600');

This is very straightforward to implement (and shouldn't mean many, or even no, BC breaks), the implementer can then provide helper methods (many of which already exist). This isn't very useful for the end user though, especially when it's not clear which implementation is being used (disparate APIs).

2) Create a HeaderInterface.

This interface allows manipulation (always accepts multiple values) such as:

$message->getHeader('Cache-Control')->add('private')->add('max-age=3600');

Interrogation, however, is limited (it would have to exactly match the values):

$message->getHeader('Cache-Control')->contains('private')

But it's hard to find out what the max-age is (unless special logic is built in for values that contain equals, and something unique for Accept).

Due to extensions it must also be possible to set headers as in 1), so the following are equivalent:

$message->getHeader('Cache-Control')->clear()->add('private');
$message->getHeader('Cache-Control')->set('private');
$message->setHeader('Cache-Control', 'private');

This is a fair bit more useful for the end user (save for the interrogation point), without being much of a problem for the implementer.

3) Create interfaces for headers that need special logic.

HeaderInterface would be the base class, then a MultipleValueHeaderInterface for those that can have multiple values (surprise!).

Headers such as Cache-Control then have their own sub-interfaces containing methods specific for that header:

$message->getCacheControlHeader()->setPrivate()->setMaxAge(3600);

Interrogation is then easy:

$message->getCacheControlHeader()->getMaxAge();

Due to extensions it must also be possible to set headers as in 1) and 2), so the following are equivalent:

$message->getCacheControlHeader()->clear()->setPrivate();
$message->getCacheControlHeader()->clear()->add('private');
$message->getCacheControlHeader()->set('private');
$message->getHeader('Cache-Control')->clear()->add('private'); // type-hint can only say HeaderInterface though
$message->getHeader('Cache-Control')->set('private'); // type-hint can only say HeaderInterface though
$message->setHeader('Cache-Control', 'private');

There could also then be interfaces for other HTTP concepts (CookieInterfaceUrlInterface etc).

This then means that the whole message is covered, rather than leaving certain parts open (so the API is now standardised, rather than everyone representing the same thing is slightly different manners).

Implementation is a lot harder and far more complex (unless you use a provided AbstractRequest/AbstractResponse), but is great for the end user since there's a guaranteed easy way of being able to do everything that they would want to. Since each existing implementation already has their own way of doing things, there will be BC problems (unless they're retained as deprecated methods, which adds to the complexity).

Thoughts?

Chris 

Chris Wilkinson

unread,
Jan 2, 2013, 11:07:56 AM1/2/13
to php...@googlegroups.com
I've just committed the package to https://github.com/thewilkybarkid/psr-http. It uses the proposal as it currently is, but as it obviously will change I'll try to keep it as up to date as possible (large changes might take a while to do!).

Chris

Hari K T

unread,
Jan 2, 2013, 11:30:09 AM1/2/13
to php...@googlegroups.com
Hey Chris, 

Good to see . Great work and effort from your side .

I had a peep into it . Do you think we need Test inside src ?


I feel the tests should reside outside in the tests folder itself .


Thanks

On Wed, Jan 2, 2013 at 9:37 PM, Chris Wilkinson <chriswil...@gmail.com> wrote:
I've just committed the package to https://github.com/thewilkybarkid/psr-http. It uses the proposal as it currently is, but as it obviously will change I'll try to keep it as up to date as possible (large changes might take a while to do!).

Chris

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msg/php-fig/-/9Rhyb8VlwbEJ.

Beau Simensen

unread,
Jan 2, 2013, 12:10:29 PM1/2/13
to php...@googlegroups.com
Great work, Chris. :)

I'm not fond of having __construct in abstract classes. I seem to recall hearing that they were going to have some weird constraints in future versions of PHP, I think the issue was that you could not change the method signature in a subclass? Unfortunately I cannot locate my reference for this. Can anyone else verify whether or not this will cause issues?

As to the Abstract classes themselves, I'm not sure whether we should have this much work being done in the PSR package. These are not simple null Request and Response implementations, they are full blown implementations with a lot of logic. If we want to provide a reference implementation in the PSR package I'd just as soon remove the abstract keyword and provide them as-is as fully functional Request and Response classes. This would solve the issue I would have with having the __construct in an abstract class.

I'm not opposed to having a reference implementation of Request and Response provided by the PSR package but I'm not sure everyone agrees that reference implementations should be provided. Vendors could still implement their own Request/Response classes from scratch.


If we have a concrete implementation of Request and Response provided by the PSR package itself I will back off of my request for including factory interfaces. Otherwise I think factories are going to be very important to low level library creators. Without factories they will have no way to instantiate new Request or Response messages without tying themselves directly to an implementation. This would negate most of the benefits of having a common HTTP Message interface.


~~~

On my query on splitting the interfaces into read/write, I'll back off on that. :)

Florin Patan

unread,
Jan 2, 2013, 12:22:02 PM1/2/13
to php...@googlegroups.com
I think Chris was following the Logger model and it's a good thing to ship the tests in the same directory with the component/interface that you are providing so that people can run them at will without too much issues.

Florin Patan

unread,
Jan 2, 2013, 12:30:33 PM1/2/13
to php...@googlegroups.com
I also agree with not having constructors/destructors locked. It's just the same discussion that we've had just a few replies up when the proposal was suggesting the same thing in the interface.

I do think that FIG should stay of the path of providing full implementations.

While the logger PSR provides some logic for it but it's no where near the full implementation nor should it be delivered with the interfaces imho. Maybe we could have a separate repository that helps this out. 

Providing the tests on the other hand is a pretty nice bonus to this as one could run the default tests and ensure that a certain implementation still follows the PSR specification.

I'll have a look on the said PR later on.


Best regards.

Hari K T

unread,
Jan 2, 2013, 12:36:12 PM1/2/13
to php...@googlegroups.com
May be you missed my point @Florin Patan . Let me try to clarify .

If you look into 


You can already see src and tests folder 



So I don't think the src should contain the tests and fixture for the right place to reside is inside tests folder. 

 
To view this discussion on the web visit https://groups.google.com/d/msg/php-fig/-/DSwL-wJMyToJ.

Florin Patan

unread,
Jan 2, 2013, 1:21:01 PM1/2/13
to php...@googlegroups.com
Hi Chris,

I've started to add comments inline to your package but I think there's much work to be done before this should be considered as a viable discussion point.
Maybe we should stick just to interfaces, like mentioned in one of my previous comments.


Best regards.

Paul Jones

unread,
Jan 2, 2013, 1:24:04 PM1/2/13
to php...@googlegroups.com

On Jan 2, 2013, at 12:21 PM, Florin Patan wrote:

> Maybe we should stick just to interfaces, like mentioned in one of my previous comments.

I have to say I agree here: hammer out the interfaces first. Only when those are "done" should we consider whether or not to provide abstracts and other stuff.


-- pmj

Paul Dragoonis

unread,
Jan 2, 2013, 2:33:29 PM1/2/13
to php...@googlegroups.com
I opened this thread with the intention to suggest that the interfaces are really evolving around the implementation we're coming up with, which is backwards, so I'm glad it's already being raised by others.

I'd like to come up with psr/http and psr/http-implementation package.

This will allow us to smoothly create nice clean interfaces for psr/http and roll that out first without any major implementation constraints, then psr/http-implementation can roll on after.

Who's with me on this idea?

Thanks,
Paul Dragoonis.


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.

Larry Garfield

unread,
Jan 2, 2013, 2:35:50 PM1/2/13
to php...@googlegroups.com
+1 on the PSR-X including only the interface definition (and possibly
some Null implementations or utility traits like Logger did). I'm fine
with this group putting out a reference implementation of the interface,
and maybe even a good one, but it should be separate from the PSR itself.

--Larry Garfield

On 1/2/13 1:33 PM, Paul Dragoonis wrote:
> I opened this thread with the intention to suggest that the interfaces
> are really evolving around the implementation we're coming up with,
> which is backwards, so I'm glad it's already being raised by others.
>
> I'd like to come up with psr/http and psr/http-implementation package.
>
> This will allow us to smoothly create nice clean interfaces for psr/http
> and roll that out first without any major implementation constraints,
> then psr/http-implementation can roll on after.
>
> Who's with me on this idea?
>
> Thanks,
> Paul Dragoonis.
>
>
> On Wed, Jan 2, 2013 at 6:24 PM, Paul Jones <pmjo...@gmail.com
> <mailto:pmjo...@gmail.com>> wrote:
>
>
> On Jan 2, 2013, at 12:21 PM, Florin Patan wrote:
>
> > Maybe we should stick just to interfaces, like mentioned in one
> of my previous comments.
>
> I have to say I agree here: hammer out the interfaces first. Only
> when those are "done" should we consider whether or not to provide
> abstracts and other stuff.
>
>
> -- pmj
>
> --
> You received this message because you are subscribed to the Google
> Groups "PHP Framework Interoperability Group" group.
> To post to this group, send email to php...@googlegroups.com
> <mailto:php...@googlegroups.com>.
> To unsubscribe from this group, send email to
> php-fig+u...@googlegroups.com
> <mailto:php-fig%2Bunsu...@googlegroups.com>.

Larry Garfield

unread,
Jan 2, 2013, 2:49:54 PM1/2/13
to php...@googlegroups.com
Hi Chris. I think this is an excellent summary of the possible
directions we could take.

My preference is to aim for a "2.5" level. Perhaps not every header
should be special cased to have its own meaningful API, but at least the
common ones should. Caching, Cookies, Accept headers, content type
headers, etc. I'm sure we could bikeshed on exactly which ones
"deserve" their own mini-API, but in general I think that's the way to go.

That implies we should probably focus on building layer 2 first (it
sounds like layers 1 and 2 would be slightly incompatible, no? I
wouldn't actually mind if we didn't have level 1), and then once that's
reasonably sorted we can itemize the possible headers to build a layer 3
implementation for.

One advantage of that approach is that we could easily extend it with
later PSRs. Eg, if in the initial spec cookies don't have a
sub-interface for managing them specifically it's reasonably
straightforward to add an extending PSR that just adds a few more
interfaces and maybe a method or two to the Message interface. We
should still try to include a reasonable set in the initial spec, IMO,
but it means we have flexibility to correct oversights later if needs be
without breaking things.

I would also encourage us to consider both workflows for
Request/Response objects:

- The HTTP Client use case (Guzzle et al), which is a PHP script SENDING
a request and RECEIVING a response;
- The HTTP Server use case (e.g. Symfony's HttpFoundation), which is a
PHP script RECEIVING a request and SENDING a response.

I would love if it we could use the same interfaces for both modes, as
that opens up plenty of possibilities for chaining, testing, proxying,
and other fun stuff.

--Larry Garfield

On 1/2/13 4:06 AM, Chris Wilkinson wrote:
> I've just made a few changes to the PR, including removing the link
> between requests/responses (at least for the moment).
>
> Re headers, I think there are 3 options.
>
> (The below isn't defining syntax, merely the styles of doing it.)
>
> *1) The bare minimum (what's there at the moment).*
>
> This has only a few methods which involve setting string values (and
> arrays that are then imploded).
>
> $message->setHeader('Cache-Control', 'private, max-age=3600');
>
> This is very straightforward to implement (and shouldn't mean many, or
> even no, BC breaks), the implementer can then provide helper methods
> (many of which already exist). This isn't very useful for the end user
> though, especially when it's not clear which implementation is being
> used (disparate APIs).
>
> *2) Create a HeaderInterface.*
>
> This interface allows manipulation (always accepts multiple values) such as:
>
> $message->getHeader('Cache-Control')->add('private')->add('max-age=3600');
>
> Interrogation, however, is limited (it would have to exactly match the
> values):
>
> $message->getHeader('Cache-Control')->contains('private')
>
> But it's hard to find out what the max-ageis (unless special logic is
> built in for values that contain equals, and something unique for Accept).
>
> Due to extensions it must also be possible to set headers as in 1), so
> the following are equivalent:
>
> $message->getHeader('Cache-Control')->clear()->add('private');
> $message->getHeader('Cache-Control')->set('private');
> $message->setHeader('Cache-Control', 'private');
>
> This is a fair bit more useful for the end user (save for the
> interrogation point), without being much of a problem for the implementer.
>
> *3) Create interfaces for headers that need special logic.*
>
> HeaderInterfacewould be the base class, then a
> MultipleValueHeaderInterfacefor those that can have multiple values
> --
> You received this message because you are subscribed to the Google
> Groups "PHP Framework Interoperability Group" group.
> To post to this group, send email to php...@googlegroups.com.
> To unsubscribe from this group, send email to
> php-fig+u...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/php-fig/-/cOJyznIWywIJ.

Paul Jones

unread,
Jan 2, 2013, 4:01:22 PM1/2/13
to php...@googlegroups.com

On Jan 2, 2013, at 1:49 PM, Larry Garfield wrote:

> Perhaps not every header should be special cased to have its own meaningful API, but at least the common ones should. Caching, Cookies, Accept headers, content type headers, etc. I'm sure we could bikeshed on exactly which ones "deserve" their own mini-API, but in general I think that's the way to go.

The easiest way to start that discussion would be to do the research on what the existing member projects do in that regard for their respective Request and Response objects. Those that commonly treat some header types as "special" are the ones to start with. Chris Wilkinson, are you up for adding that on to your workload?


-- pmj

Paul Dragoonis

unread,
Jan 2, 2013, 4:04:26 PM1/2/13
to php...@googlegroups.com
Paul,

I'm sure Chris done it already, I think it was just highlighting the libs that have their own Req/Resp implementations, rather than special classes for things like headers/Cookies. I'm sure you're aware of this, so I believe the libs that Chris initially mentioned should be the ones considered for the survey here.

</$0.02>


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.

Chris Wilkinson

unread,
Jan 3, 2013, 3:12:54 AM1/3/13
to php...@googlegroups.com
On Sunday, December 30, 2012 10:24:40 PM UTC, Michael Dowling wrote:
> I would agree with not having a modify-body operation other than get/set.  However, I do think that support for streaming responses is important in many situations so we should try to include it if possible (or maybe as an extended interface if people prefer). Passing in a callable or, in PHP 5.5, a generator (that could be fun) is the best way to handle large returns without blowing out your memory, so let's make sure that's supported.

Yes, support for streaming is very important. That's why I mentioned
Guzzle passes this responsibility off to an EntityBody object to
encapsulate the logic of streaming data onto or off of a data source.
An HTTP PSR could either do something similar (e.g. use an EntityBody
object) or account for various data types passed into the setBody()
method.

Looking over setBody() again I think this approach is far better. The only requirement the interface should have is that it's possible to make the body a string, so the base requirement should only be a string or an object that implements __toString() (I'm not sure a BodyInterface would have anything else, so it's not really needed). This means that an implementation doesn't have to worry about things like streams unless they need to, and it should always be possible for an implementation to accept other types that they know how to deal with (so for streams, for example, you can choose to put the logic elsewhere (ie in an object) or in the message itself).

Chris

Chris Wilkinson

unread,
Jan 3, 2013, 4:48:08 AM1/3/13
to php...@googlegroups.com
Hi Larry,

I agree about the 2.5 level, I wasn't trying to suggest that every header should have a special implementation (eg Allow is just plain values, and I can't imagine Via is interrogated at code-level). It possibly should be a little more wide-ranging that most implementations currently are though (for example recently I've needed to tell if a response has a 111 Revalidation failed warning in Guzzle, which is a little difficult as I have to scan the values manually, rather than being able to do something like $message->getWarningHeader->hasWarning(111) - I mean that as something to consider generally rather than a specific requirement!).

Re level 1, you're right, that could be dropped as long as level 2 is present (there must be a way to set custom headers/values).

I've quickly put together an example interface at https://gist.github.com/4441924. This allows multiple values on all headers, which isn't right, but is very straightforward.

A second example is at https://gist.github.com/4442127, which contains two types (a base single-value interface, and a sub multi-value one). So $message->getHeader('Expires') would return a HeaderInterface and $message->getHeader('Cache-Control') a MultiValueHeaderInterface.

The latter would save probably having to break BC if sub-interfaces (eg ExpiresHeaderInterface extends HeaderInterface and CacheControlInterface extends MultiValueHeaderInterface) and respective methods (eg getExpiresHeader() and getCacheControlHeader()) are added later on.

Re both workflows, that was the primary reason that prompted me to do this (see the Symfony issue in the opening post)!

Chris

Chris Wilkinson

unread,
Jan 11, 2013, 5:32:27 AM1/11/13
to php...@googlegroups.com
On Wednesday, 2 January 2013 21:01:22 UTC, pmjones wrote:
The easiest way to start that discussion would be to do the research on what the existing member projects do in that regard for their respective Request and Response objects.  Those that commonly treat some header types as "special" are the ones to start with.  Chris Wilkinson, are you up for adding that on to your workload? 

I've gone through the libraries (I added Nette too) and tried to recognise where there special methods/objects for headers in some form (without judgement on how useful/powerful they are):

https://docs.google.com/spreadsheet/ccc?key=0AheXAl1mhspddE1QdmxoSVltZ0hCbmFRVVc0cXpOckE (this should be editable too, so if I've got something wrong please just change it - I haven't directly used them all, I've just skimmed through code)

For responses, you can reasonably say that caching/cookies are the ones of definite interest.

For requests, however, it's a lot more disparate.

I'm wondering whether, for an initial release, to not have any special header functions (so just raw setHeader() and getHeader() methods) and let implementations keep their own helper methods, then possibly add some in a later version?

Chris Wilkinson

unread,
Jan 21, 2013, 8:20:41 AM1/21/13
to php...@googlegroups.com
Any thoughts on how to handle headers?

Also, in case you haven't spotted, I've started up a separate discussion on URLs/URIs which could result in the getUrl() method becoming more useful (ie not just a string).

Chris

Paul Jones

unread,
Jan 21, 2013, 9:51:28 AM1/21/13
to php...@googlegroups.com

On Jan 21, 2013, at 7:20 AM, Chris Wilkinson wrote:

> Any thoughts on how to handle headers?

Ah, yes: although I'd like to see a Cookie/CookieCollection object, and a Header/HeaderCollection, I think we can get away without them *for now*. I think it would be enough to get started with an array of arrays for the header data structure, something like this:

$headers = [
'Header-Name-1' => [
['value'],
]
'Header-Name-2' => [
['value 1'],
['value 2'],
['value 3'],
]
];

Getting out headers would be an exercise in looping through both of them appropriately.

Thoughts?


-- pmj

Moisa Teodor

unread,
Jan 21, 2013, 12:20:50 PM1/21/13
to php...@googlegroups.com

On Mon, Jan 21, 2013 at 4:51 PM, Paul Jones <pmjo...@gmail.com> wrote:
I think we can get away without them *for now*.  I think it would be enough to get started with an array of arrays for the header data structure, something like this:

    $headers = [
        'Header-Name-1' => [
            ['value'],
        ]
        'Header-Name-2' => [
            ['value 1'],
            ['value 2'],
            ['value 3'],
        ]
    ];

Getting out headers would be an exercise in looping through both of them appropriately.

Thoughts?

Since the header names are case insensitive, I don't believe an array is the right structure, unless it is processed and the values merged. For example:

$headers = [
    'Content-Type' => ['text/plain'],
    'content-type'   => ['application/json']
];

For headers that accept mutiple values (and parameters) - a merge would do the trick, however, like in the above example, which value should be used - 'text/html' or 'application/json', as there can be only one content type header ?



--
Doru Moisa
web: http;//moisadoru.wordpress.com
tel: +40 720 861 922
Bucharest, Romania

Larry Garfield

unread,
Jan 21, 2013, 7:07:29 PM1/21/13
to php...@googlegroups.com
That seems more like an internal implementation detail. I would
absolutely not want to expose an array structure like that as the
interface itself. (I come from a Drupal background. Trust me on this.
Big nested arrays do not an API make. <g>)

I can see the argument to not bother with a bunch of collection objects
yet (maybe as the spec matures before we finalize it), but at minimum
the interaction with the package should be through for-reals methods,
even if they return arrays for that particular header.

--Larry Garfield

Ryan McCue

unread,
Jan 21, 2013, 7:18:27 PM1/21/13
to php...@googlegroups.com
Paul Jones wrote:
>
> On Jan 21, 2013, at 7:20 AM, Chris Wilkinson wrote:
>
>> Any thoughts on how to handle headers?
>
> Ah, yes: although I'd like to see a Cookie/CookieCollection object, and a Header/HeaderCollection, I think we can get away without them *for now*. I think it would be enough to get started with an array of arrays for the header data structure, something like this:

Requests handles them by converting multiple headers of the same type
into a single string representation. RFC2616 notes that the headers can
simply be concatenated with a comma. It's a silly quirk, but it has
worked. Repeated headers are fairly rare in the HTTP world (as opposed
to email), being mostly limited to things which already handle commas
(link header parsers, e.g.).

Ideally: a HeaderCollection object that implements ArrayAccess. Headers
are accessed case-insensitively (as per spec) and each header is always
a string. To get at the raw array, add a getHeader($key) method that
always returns an array as per Paul's suggestion.

Chris Wilkinson

unread,
Jan 22, 2013, 4:18:08 AM1/22/13
to php...@googlegroups.com
I think the PSR should state that calling getHeader('Cache-Control') (or whatever) must return all the Cache-Control header(s) values as one, but may choose not to store the info as such (so if you output the raw message they would still appear over multiple headers). I don't think it's the PSR's place to enforce merging them (but I can't see any reason why anyone wouldn't!).

I don't mind whether there's a HeaderCollectionInterface or they're accessed directly from the message, but I'm not sure that all headers should have an array as their value (some are explicitly single values). Did anyone take a look at my two HeaderInterface suggestions a few posts back?

Chris

Chris Wilkinson

unread,
Apr 4, 2013, 4:09:25 PM4/4/13
to php...@googlegroups.com
Bump! :)

The areas still to discuss I think are:
  • Headers
  • URI (which would use the seperate URI PSR if agreed)
  • Cookies
Chris

Phil Sturgeon

unread,
May 1, 2013, 11:19:02 AM5/1/13
to php...@googlegroups.com
I'm torn between agreeing with Larry and just saying "sod it, let the implementation work out what to do with multiples" and thinking we'd really be keeping it too vague. If one implementation decides to blot out the existing header and replace it with a new header, then a lot of applications are gonna break if they use a different package, even if its spec compliant.

Ryan/Pauls suggestion seems good:

Ideally: a HeaderCollection object that implements ArrayAccess. Headers 
are accessed case-insensitively (as per spec) and each header is always 
a string. To get at the raw array, add a getHeader($key) method that 
always returns an array as per Paul's suggestion. 

I think that works quite nicely:

$headers = new HeaderCollection([
    'Content-Type' => 'application/json';
]);
$headers->setHeader('Cache-Control', 'nocache');
$headers->setHeader('Content-Type', 'text/json');

The header items themselves dont need to be stored as arrays, just strings that get concatenated with "," as Ryan pointed out, it's perfectly compliant with RFC2616.

Evert Pot

unread,
May 1, 2013, 11:23:48 AM5/1/13
to php...@googlegroups.com
Ahh but being perfectly compliant is one thing, assuming everyone else is, is another :)

Another example :

Header names cannot be normalized to lower-case, you must preserve the original casing. Even though compliant consumers should treat a header name as case-insenstive, unfortunately many consumers do expect a certain casing.

Evert

Phil Sturgeon

unread,
May 1, 2013, 3:13:09 PM5/1/13
to php...@googlegroups.com
Ahh but being perfectly compliant is one thing, assuming everyone else is, is another :) 

I'm not sure what you mean. If PyroCMS interacts with a PSR\HTTP implementation and passes it two headers assuming its going to do A and it does A, then another compliant package uses the exact same PSR\HTTP interface but does B... well, thats completely useless.

Evert Pot

unread,
May 1, 2013, 3:16:25 PM5/1/13
to php...@googlegroups.com
On May 1, 2013, at 8:13 PM, Phil Sturgeon <em...@philsturgeon.co.uk> wrote:

> Ahh but being perfectly compliant is one thing, assuming everyone else is, is another :)
>
> I'm not sure what you mean. If PyroCMS interacts with a PSR\HTTP implementation and passes it two headers assuming its going to do A and it does A, then another compliant package uses the exact same PSR\HTTP interface but does B... well, thats completely useless.

Sorry..

I meant that it's important to be able to control wether headers and concatenated using a comma, or split as into multiple headers.

So as long as that condition is satisfied, I'd be happy :)

Evert


omissis

unread,
May 3, 2013, 10:07:31 AM5/3/13
to php...@googlegroups.com
Hi guys,
  I tried to put together some of the code and ideas found in this thread in this repo: https://github.com/omissis/psr-http
It builds on top of @thewilkybarkid PR adding Factories, as Beau Simensen suggested, and Interfaces for headers.
I've also took inspiration from the google doc by @thewilkybarkid and added a set of header interfaces as they can be found here: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
That's probably a reliable list of headers the standard can rely on, at least for the beginning.
The idea is to have the tiny, specific header interfaces to be optional, so that client libraries can decide what to implement.

Thoughts? thx :)


On Saturday, December 29, 2012 11:45:40 PM UTC+1, Chris Wilkinson wrote:
Hi,

There was a proposal to create a HTTP client interface earlier this year. As was noted at the time, the core HTTP message representation part (ie the request/response objects) is probably of more importance: it's used by both HTTP clients and frameworks.

HTTP clients issue requests (through cURL for example) and return the response to the user. Some frameworks (eg Symfony) convert superglobals into a request object and let the user return a response object (which is then returned to the browser).

It is actually possible to connect the two (eg for functional testing). Disparate objects, however, create problems (for example, the Symfony BrowserKit and HttpKernel components have their own Response classes - see https://github.com/symfony/symfony/issues/4475).

Because of this, there should be a standard RequestInterface and ResponseInterface. The ClientInterface can then be developed separately, obviously making use of them.

For reference, a non-exhaustive list of existing request/response object implementations:

Symfony2 HttpFoundation

Symfony2 BrowserKit

Guzzle

Zend2

Buzz

Requests

Aura

CakePHP

I'd like to propose a Psr\Http package containing something along the lines of:

interface MessageInterface
{
    public function __toString();
    public function getStartLine();
    public function getProtocolVersion();
    public function setProtocolVersion($protocolVersion);
    public function getHeader($header);
    public function getHeaders();
    public function hasHeader($header);
    public function setHeader($header, $value);
    public function setHeaders(array $headers);
    public function addHeaders(array $headers);
    public function getBody();
    public function setBody($body);
}

interface RequestInterface extends MessageInterface
{
    public function __construct($method, $url, $protocolVersion = '1.1');
    public function getResponse();
    public function setResponse(ResponseInterface $response);
    public function getMethod();
    public function setMethod($method);
    public function getUrl();
    public function setUrl($url);
}

interface ResponseInterface extends MessageInterface
{
    public function __construct($statusCode, $reasonPhrase = null, $protocolVersion = '1.1');
    public function getRequest();
    public function setRequest(RequestInterface $request);
    public function getStatusCode();
    public function setStatusCode($statusCode);
    public function getReasonPhrase();
    public function setReasonPhrase($reasonPhrase);
}

This provides the basics for creating and interrogating request/response objects manually, and producing a HTTP message by casting it to a string.

The headers aren't that easy to use though, as you have to set the entire value at once (and seeing what's been set is hard). Most existing implementations provide helper methods for some (if not most) of the headers as a result.

So instead of:

$response->setHeader('Cache-Control', 'private, max-age=3600, must-revalidate');

You could do:

$response->getCacheControlHeader()->setPrivate()->setMaxAge(3600)->setMustRevalidate();

or even just:

$response->setPrivate()->setMaxAge(3600)->setMustRevalidate();

If these are added to the interfaces somehow, they should cover all standard headers (extensions can still be set manually). This does dramatically increase the complexity of implementation though (although you could just extend a provided abstract class, I can't really think of any need for variation), and has possibly extended the remit.

Any thoughts?

Chris (aka thewilkybarkid)
Reply all
Reply to author
Forward
0 new messages