Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

HTTP message proposal #72

Merged
merged 6 commits into from

10 participants

@thewilkybarkid

This follows on from the discussion at https://groups.google.com/forum/?fromgroups=#!topic/php-fig/73cM2qq_uho

It provides basic descriptions for a MessageInterface, RequestInterface and ResponseInterface. These will need expanding, including how to use them.

@thewilkybarkid

I have almost completed a HTTP message package too (including abstract request/response classes). This isn't on GitHub yet.

proposed/http.md
((224 lines not shown))
+ */
+interface RequestInterface extends MessageInterface
+{
+ /**
+ * Returns the request as an HTTP string.
+ *
+ * @return string Request as an HTTP string.
+ */
+ public function __toString();
+
+ /**
+ * Gets the response object that is associated with this request.
+ *
+ * @return ResponseInterface|null Response, or null if not set.
+ */
+ public function getResponse();

A request may be re-used multiple times, and the response may be different each time. I think this assumption is wrong.

@simensen Collaborator

Ditto. I mentioned this on the ML.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
proposed/http.md
((233 lines not shown))
+
+ /**
+ * Gets the response object that is associated with this request.
+ *
+ * @return ResponseInterface|null Response, or null if not set.
+ */
+ public function getResponse();
+
+ /**
+ * Sets the response object that is associated with this request.
+ *
+ * @param ResponseInterface $response Response.
+ *
+ * @return RequestInterface Reference to the request.
+ */
+ public function setResponse(ResponseInterface $response);

Same as above. Not sure why a request would be aware of a response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
proposed/http.md
((279 lines not shown))
+ *
+ * @throws InvalidArgumentException If the URL is invalid.
+ */
+ public function setUrl($url);
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return RequestInterface Reference to the request.
+ */
+ public function setProtocolVersion($protocolVersion);
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return RequestInterface Reference to the request.

Instead of using all these repetitions, you can simply skip those and instead set the return type to self in the parent interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
proposed/http.md
((332 lines not shown))
+ */
+interface ResponseInterface extends MessageInterface
+{
+ /**
+ * Returns the response as an HTTP string.
+ *
+ * @return string Response as an HTTP string.
+ */
+ public function __toString();
+
+ /**
+ * Gets the request object that is associated with this response.
+ *
+ * @return RequestInterface|null Request, or null if not set.
+ */
+ public function getRequest();

Same as above. Response has no reason to know a request that caused it. It's a response, it bubbles up across layers of your application regardless of where the request got stuck.

@simensen Collaborator

Ditto. I mentioned this on the ML.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
proposed/http.md
((390 lines not shown))
+ */
+ public function getReasonPhrase();
+
+ /**
+ * Sets the response reason phrase.
+ *
+ * @param string $reasonPhrase Reason phrase.
+ *
+ * @return ResponseInterface Reference to the response.
+ */
+ public function setReasonPhrase($reasonPhrase);
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return ResponseInterface Reference to the response.

Same as above with repetitions. Fix the return type in the parent interfaces by using @return self

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@thewilkybarkid

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

@marijn

I really hate the fact that these interfaces promote mutability. I would argue they should be modeled as value objects...

@simensen
Collaborator

@marijn I have similar feelings and thought it would be nice to treat request and response as value objects. However, the idea was put forth that for observers/plugins and such it would make sense for requests and responses to be able to be changed.

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). — @mtdowling

@marijn

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

Which is exactly my point, it is no longer the same request and should not be treated as such.

@Ocramius

@simensen even if it's hard, the interface can give a direction on that by just defining getters for example... That would be enough to allow implementation of immutables

@simensen
Collaborator

@Ocramius Don't get me wrong, I'm a big fan of the idea and proposed how I'd like to see the interfaces laid out on the mailing list. :) Since it was brought up here independently I wanted to share the concerns @mtdowling's expressed on the mailing list about the idea of splitting the interface into read/write interfaces.

Sending a read-only Request instance to something like Guzzle would require Guzzle to transform the entire Request into a native internal writable Request (whether it be based on PSR HTTP Message interface or not) in order to be able to function correctly since it expects to be able to write to the Request before it has been processed.

@mtdowling
@staabm staabm commented on the diff
proposed/http.md
((274 lines not shown))
+/**
+ * A request message from a server to a client.
+ */
+interface ResponseInterface extends MessageInterface
+{
+ /**
+ * Gets the response status code.
+ *
+ * @return string Status code.
+ */
+ public function getStatusCode();
+
+ /**
+ * Sets the response status code.
+ *
+ * @param integer $statusCode Status code.
@staabm
staabm added a note

getStatusCode returns a string,...

Good spot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@staabm staabm commented on the diff
proposed/http.md
((286 lines not shown))
+ /**
+ * Sets the response status code.
+ *
+ * @param integer $statusCode Status code.
+ *
+ * @return self Reference to the response.
+ *
+ * @throws InvalidArgumentException When the status code is not valid.
+ */
+ public function setStatusCode($statusCode);
+
+ /**
+ * Gets the response reason phrase.
+ *
+ * If it has not been explicitly set using `setReasonPhrase()` it SHOULD
+ * return the RFC 2616 recommended reason phrase.
@staabm
staabm added a note

Should this PSR contain a class which hold all http status codes and reason phrases?

@Ocramius
Ocramius added a note

Would be VERY useful, but I think it's not really needed, and it would also be hard to have an agreement on it. Interfaces for basic exceptions (4xx, 5xx) may be interesting.

It would be useful to have, but should be in an abstract implementation if anywhere. As for exception interfaces, I think they should be left to implementers if they want them (not sure they add that much, and it would also be very hard to agree on which ones!).

I agree that exceptions should be left to implementations. IMO, there's no need to define them in this PSR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bakura10 bakura10 referenced this pull request in aws/aws-sdk-php-zf2
Closed

S3 signature #5

@BenMorel BenMorel commented on the diff
proposed/http.md
((181 lines not shown))
+ *
+ * The body SHOULD be a string, or an object that implements the
+ * `__toString()` method.
+ *
+ * A null value will remove the existing body.
+ *
+ * An implementation MAY accept other types, but MUST reject anything that
+ * it does not know how to turn into a string.
+ *
+ * @param mixed $body Body.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When the body is not valid.
+ */
+ public function setBody($body);

It think that to become a standard, the interface must support streamable bodies : you can't just expect an object to be castable to string. Imagine you're transferring a 100MB file in the body, it won't fit in memory, so this must accept a stream as well.
An alternative would be to create a MessageBody interface, that would expose methods such as read() etc.

@staabm
staabm added a note

Good point!

I don't think stream handling should be enforced here, that would stop adoption by lightweight implementers such as Buzz.

Instead, It would be useful to have a StreamInterface defined in a separate PSR (Guzzle and Drupal, for example, have one of these already). Implementers of this interface can then choose to handle StreamInterface objects differently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mtdowling mtdowling commented on the diff
proposed/http.md
((127 lines not shown))
+ *
+ * The header names and values MUST strings, or objects that implement the
+ * `__toString()` method. The values MAY also be arrays, in which case they
+ * MUST be converted to comma-separated strings; the ordering MUST be
+ * maintained.
+ *
+ * @param array $headers Headers to set.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When part of the header set is not valid.
+ */
+ public function setHeaders(array $headers);
+
+ /**
+ * Adds headers, replacing those that are already set.

I think that this method shouldn't replace headers that are already set, but instead merge into the existing headers. For example, if there is already a Cache-Control header set to "public", and I call addHeaders(['Cache-Control' => 'max-age=2592000']), then the Cache-Control header should become public, max-age=2592000.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mtdowling mtdowling commented on the diff
proposed/http.md
((136 lines not shown))
+ *
+ * @throws InvalidArgumentException When part of the header set is not valid.
+ */
+ public function setHeaders(array $headers);
+
+ /**
+ * Adds headers, replacing those that are already set.
+ *
+ * The array keys must the header name, the values the header value.
+ *
+ * The header names and values MUST strings, or objects that implement the
+ * `__toString()` method. The values MAY also be arrays, in which case they
+ * MUST be converted to comma-separated strings; the ordering MUST be
+ * maintained.
+ *
+ * Null values will remove existing headers.

Why is this in the specification?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mtdowling mtdowling commented on the diff
proposed/http.md
((67 lines not shown))
+ * Sets the HTTP protocol version.
+ *
+ * @param string $protocolVersion The HTTP protocol version.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When the HTTP protocol version is not valid.
+ */
+ public function setProtocolVersion($protocolVersion);
+
+ /**
+ * Gets a header.
+ *
+ * @param string $header Header name.
+ *
+ * @return string|null Header value, or null if not set.

Should this mention that the return value can be a string or an object that implements __toString()?

@BenMorel
BenMorel added a note

IMO string is fine, it implicitly means that whatever the implementation chooses to return, it must be castable as a string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mtdowling mtdowling commented on the diff
proposed/http.md
((26 lines not shown))
+
+The interfaces and classes described as well as relevant exception classes and
+a test suite to verify your implementation are provided as part of the
+[psr/http](https://packagist.org/packages/psr/http) package.
+
+3. Interfaces
+-------------
+
+### 3.1 `Psr\Http\MessageInterface`
+
+```php
+<?php
+
+namespace Psr\Http;
+
+use Psr\Http\Exception\InvalidArgumentException;

I don't see the value in adding a marker type interface for an InvalidArgumentException. I've errantly done this in the past myself, and I now see that this sort of exception marking has no value when an existing standard library exception provides the exact same semantic meaning. (See: http://my.safaribooksonline.com/book/programming/java/9780137150021/exceptions/ch09lev1sec4)

One benefit is to be able to filter/sort exceptions for a given component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mtdowling mtdowling commented on the diff
proposed/http.md
((76 lines not shown))
+
+ /**
+ * Gets a header.
+ *
+ * @param string $header Header name.
+ *
+ * @return string|null Header value, or null if not set.
+ */
+ public function getHeader($header);
+
+ /**
+ * Gets all headers.
+ *
+ * The array keys are the header name, the values the header value.
+ *
+ * @return array Headers.

How would someone use a simple key/value pair array that is returned from this function when HTTP headers are meant to be case-insensitive? When using a simple array, you either have multiple array keys of the same name with different casing (e.g. "Foo", "foo") or you have to put something like "use the lowercase name of the header to retrieve a header from the array". I think more thought need to be put into the return value of this method in order for this method to be used consistently across different implementations.

Guzzle returns an object called HeaderCollection that implements \ArrayAccess, \IteratorAggregate, and Countable. This header collection is basically a case insensitive dictionary that returns objects implementing HeaderInterface. HeaderInterface is a class implements __toString, has a "name" property, and allows multiple headers values to be specified. When cast to a string, the header values are imploded on the header's "glue" variable.

In Symfony, your HeaderCollection is called a HeaderBag. I think we should rely on such object instead of an array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@willdurand willdurand commented on the diff
proposed/http.md
((87 lines not shown))
+ * Gets all headers.
+ *
+ * The array keys are the header name, the values the header value.
+ *
+ * @return array Headers.
+ */
+ public function getHeaders();
+
+ /**
+ * Checks if a certain header is present.
+ *
+ * @param string $header Header name.
+ *
+ * @return bool If the header is present.
+ */
+ public function hasHeader($header);

We could expose the headers as an object (see above) only, so that this method and the one right below would not be part of this interface (Demeter's Law etc.)

+1

Even without the headers object, checking if the header exists is trivial through getHeaders()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@willdurand willdurand commented on the diff
proposed/http.md
((198 lines not shown))
+```
+
+### 3.2 `Psr\Http\RequestInterface`
+
+```php
+<?php
+
+namespace Psr\Http;
+
+use Psr\Http\Exception\InvalidArgumentException;
+
+/**
+ * A request message from a client to a server.
+ */
+interface RequestInterface extends MessageInterface
+{

Maybe this interface could declare some constants, such as the HTTP verbs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius Ocramius commented on the diff
proposed/http.md
((144 lines not shown))
+ * The array keys must the header name, the values the header value.
+ *
+ * The header names and values MUST strings, or objects that implement the
+ * `__toString()` method. The values MAY also be arrays, in which case they
+ * MUST be converted to comma-separated strings; the ordering MUST be
+ * maintained.
+ *
+ * Null values will remove existing headers.
+ *
+ * @param array $headers Headers to add.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When part of the header set is not valid.
+ */
+ public function addHeaders(array $headers);

As @mtdowling has described above, this method seems to enforce too much behaviour that could live in either a headers container. setHeader and addHeader would be better at this.

Agreed. This method could be removed IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius Ocramius commented on the diff
proposed/http.md
((155 lines not shown))
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When part of the header set is not valid.
+ */
+ public function addHeaders(array $headers);
+
+ /**
+ * 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();

Not sure why the body of an HTTP message would be anything different from a string. In HTTP, it's always a string, the fact that it's json encoded or url encoded is a different matter that can be handled in getDecodedBody or something similar.

I agree there should not be a getBodyAsString() method. IMO, we should force getBody() to return a BodyInterface, which among others would offer the __toString() method for those who want to transparently use getBody() as if it returned a string. This interface could also offer methods to stream the output.

@ghost
ghost added a note

A few Actions That has a Laminator
Buy Fake Fendi Handbag http://www.mcexplorer.com/Fendi-Handbags-For-Sale-Replica/

Please, Keep It Simple.. The body of a HTTP request is a string, getBody() should return a string value, period.

There is no need for a BodyInterface or whatever.

I think it should just be what was given to setBody() (or during construction), so by default it can be either a string/stringable object, but it could be something else if the implementation allows it (native stream or whatever). That should be flexible but simple.

@mtdowling has a point though. What happens if you got large streamable contents?

@sebpot It's up to the implementation, small HTTP messages could return new StringBody($string); while an Amazon S3 response could return new StreamBody($stream); for example. The whole purpose of the interface is to be able to access it in a unified manner, without having to wonder about the actual type (and do dirty things such as if (is_string($body)) {} else if(is_resource($body)) {}).
Note that when I suggest an interface, it could actually be a plain class as well, such as MessageBody. We could provide several factory methods, such as MessageBody::fromString() and MessageBody::fromStream().

@willdurand Did you read @mtdowling's comment above? It would be just impossible to use this interface for large objects such as the ones returned by Amazon S3. But if as I suggest, getBody() returns a class/interface that declares __toString(), then you'll be free, as a consumer of the library, to use it just as if it returned a plain string.

@thewilkybarkid Unfortunately, that would lead to dirty conditionals to deal with the body, as I mentioned above.

@ghost
ghost added a note

Hello, Neat post. There is a problem along with your website in internet explorer, may test this? IE still is the marketplace leader and a huge portion of folks will miss your fantastic writing because of this problem.
[url=http://www.eurocontrol.es/newsletters/consultoria/catalogo/calzoncillos-de-calvin-klein-baratos.htm]Calzoncillos De Calvin Klein Baratos[/url]

Ah no, I missed last @mtdowling's comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius

Looking back at the various setters in the interface, I'm wondering if they should be there at all.

This goes back to my idea of allowing immutable implementations of the various message objects.

Was there some kind of consensus on this? I don't see why setters would need to be implemented by libraries adopting such a PSR. Assuming that I wanted to provide a new message and pass it to another library, I could do something like (pseudo):

$originalMessage = $someOtherEndpoint->gimmeMessage();

$myMessage = new MyMessage();
$myMessage->setWhatever($originalMessage);

$someOtherEndpoint->sendMessage($myMessage);

As long as getter implementation is respected, the $someOtherEndpoint should be able to understand $myMessage.

@thewilkybarkid

@Ocramius At certain points I think immutable messages make sense (eg the user's request/response for a sub-request in a controller), but they usually need to go through layers (eg cache) where they can be modified. As @mtdowling said, I don't know of any library/framework that actually has immutable request/response objects.

@Ocramius

@thewilkybarkid the fact that most libs take the "mutable" way doesn't mean that interfaces should be extended to setters.

@ghost

I spared a thought that would be better to ask you here before pulling the request.

Just wrote some interfaces and need your opinion before doing with this anything else. Look at sebpot/Http and tell me if there's any sense in proposing this package here as PSR, and if so, what corrections should I do.

@mtdowling

I've submitted a new PR that is a (mostly rewritten) fork of this PR with changes that I feel are necessary to provide a meaningful PSR for HTTP messages.

#244

@philsturgeon philsturgeon merged commit 7d93894 into php-fig:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 31, 2012
  1. @thewilkybarkid
Commits on Jan 2, 2013
  1. @thewilkybarkid
  2. @thewilkybarkid

    Use @return self

    thewilkybarkid authored
  3. @thewilkybarkid
Commits on Jan 8, 2013
  1. @thewilkybarkid

    Return type fix

    thewilkybarkid authored
  2. @thewilkybarkid

    Simplify body types

    thewilkybarkid authored
This page is out of date. Refresh to see the latest.
Showing with 301 additions and 0 deletions.
  1. +301 −0 proposed/http.md
View
301 proposed/http.md
@@ -0,0 +1,301 @@
+HTTP message interfaces
+=======================
+
+This document describes common interfaces for representing HTTP messages.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Specification
+----------------
+
+### 1.1 Basics
+
+HTTP messages consist of requests from client to server and responses from
+server to client. These are represented by `Psr\Http\RequestInterface` and
+`Psr\Http\ResponseInterface` respectively.
+
+Both message types extend `Psr\Http\MessageInterface`, which MUST not be
+implemented directly.
+
+2. Package
+----------
+
+The interfaces and classes described as well as relevant exception classes and
+a test suite to verify your implementation are provided as part of the
+[psr/http](https://packagist.org/packages/psr/http) package.
+
+3. Interfaces
+-------------
+
+### 3.1 `Psr\Http\MessageInterface`
+
+```php
+<?php
+
+namespace Psr\Http;
+
+use Psr\Http\Exception\InvalidArgumentException;

I don't see the value in adding a marker type interface for an InvalidArgumentException. I've errantly done this in the past myself, and I now see that this sort of exception marking has no value when an existing standard library exception provides the exact same semantic meaning. (See: http://my.safaribooksonline.com/book/programming/java/9780137150021/exceptions/ch09lev1sec4)

One benefit is to be able to filter/sort exceptions for a given component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+/**
+ * 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 $protocolVersion The HTTP protocol version.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When the HTTP protocol version is not valid.
+ */
+ public function setProtocolVersion($protocolVersion);
+
+ /**
+ * Gets a header.
+ *
+ * @param string $header Header name.
+ *
+ * @return string|null Header value, or null if not set.

Should this mention that the return value can be a string or an object that implements __toString()?

@BenMorel
BenMorel added a note

IMO string is fine, it implicitly means that whatever the implementation chooses to return, it must be castable as a string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ */
+ public function getHeader($header);
+
+ /**
+ * Gets all headers.
+ *
+ * The array keys are the header name, the values the header value.
+ *
+ * @return array Headers.

How would someone use a simple key/value pair array that is returned from this function when HTTP headers are meant to be case-insensitive? When using a simple array, you either have multiple array keys of the same name with different casing (e.g. "Foo", "foo") or you have to put something like "use the lowercase name of the header to retrieve a header from the array". I think more thought need to be put into the return value of this method in order for this method to be used consistently across different implementations.

Guzzle returns an object called HeaderCollection that implements \ArrayAccess, \IteratorAggregate, and Countable. This header collection is basically a case insensitive dictionary that returns objects implementing HeaderInterface. HeaderInterface is a class implements __toString, has a "name" property, and allows multiple headers values to be specified. When cast to a string, the header values are imploded on the header's "glue" variable.

In Symfony, your HeaderCollection is called a HeaderBag. I think we should rely on such object instead of an array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ */
+ public function getHeaders();
+
+ /**
+ * Checks if a certain header is present.
+ *
+ * @param string $header Header name.
+ *
+ * @return bool If the header is present.
+ */
+ public function hasHeader($header);

We could expose the headers as an object (see above) only, so that this method and the one right below would not be part of this interface (Demeter's Law etc.)

+1

Even without the headers object, checking if the header exists is trivial through getHeaders()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ /**
+ * Sets a header, replacing the existing header if has already been set.
+ *
+ * The header name and value MUST be a string, or an object that implement
+ * the `__toString()` method. The value MAY also be an array, in which case
+ * it MUST be converted to a comma-separated string; the ordering MUST be
+ * maintained.
+ *
+ * A null value will remove the existing header.
+ *
+ * @param string $header Header name.
+ * @param string $value Header value.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When the header name or value is not valid.
+ */
+ public function setHeader($header, $value);
+
+ /**
+ * Sets headers, removing any that have already been set.
+ *
+ * The array keys must the header name, the values the header value.
+ *
+ * The header names and values MUST strings, or objects that implement the
+ * `__toString()` method. The values MAY also be arrays, in which case they
+ * MUST be converted to comma-separated strings; the ordering MUST be
+ * maintained.
+ *
+ * @param array $headers Headers to set.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When part of the header set is not valid.
+ */
+ public function setHeaders(array $headers);
+
+ /**
+ * Adds headers, replacing those that are already set.

I think that this method shouldn't replace headers that are already set, but instead merge into the existing headers. For example, if there is already a Cache-Control header set to "public", and I call addHeaders(['Cache-Control' => 'max-age=2592000']), then the Cache-Control header should become public, max-age=2592000.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ *
+ * The array keys must the header name, the values the header value.
+ *
+ * The header names and values MUST strings, or objects that implement the
+ * `__toString()` method. The values MAY also be arrays, in which case they
+ * MUST be converted to comma-separated strings; the ordering MUST be
+ * maintained.
+ *
+ * Null values will remove existing headers.

Why is this in the specification?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ *
+ * @param array $headers Headers to add.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When part of the header set is not valid.
+ */
+ public function addHeaders(array $headers);

As @mtdowling has described above, this method seems to enforce too much behaviour that could live in either a headers container. setHeader and addHeader would be better at this.

Agreed. This method could be removed IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ /**
+ * 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();

Not sure why the body of an HTTP message would be anything different from a string. In HTTP, it's always a string, the fact that it's json encoded or url encoded is a different matter that can be handled in getDecodedBody or something similar.

I agree there should not be a getBodyAsString() method. IMO, we should force getBody() to return a BodyInterface, which among others would offer the __toString() method for those who want to transparently use getBody() as if it returned a string. This interface could also offer methods to stream the output.

@ghost
ghost added a note

A few Actions That has a Laminator
Buy Fake Fendi Handbag http://www.mcexplorer.com/Fendi-Handbags-For-Sale-Replica/

Please, Keep It Simple.. The body of a HTTP request is a string, getBody() should return a string value, period.

There is no need for a BodyInterface or whatever.

I think it should just be what was given to setBody() (or during construction), so by default it can be either a string/stringable object, but it could be something else if the implementation allows it (native stream or whatever). That should be flexible but simple.

@mtdowling has a point though. What happens if you got large streamable contents?

@sebpot It's up to the implementation, small HTTP messages could return new StringBody($string); while an Amazon S3 response could return new StreamBody($stream); for example. The whole purpose of the interface is to be able to access it in a unified manner, without having to wonder about the actual type (and do dirty things such as if (is_string($body)) {} else if(is_resource($body)) {}).
Note that when I suggest an interface, it could actually be a plain class as well, such as MessageBody. We could provide several factory methods, such as MessageBody::fromString() and MessageBody::fromStream().

@willdurand Did you read @mtdowling's comment above? It would be just impossible to use this interface for large objects such as the ones returned by Amazon S3. But if as I suggest, getBody() returns a class/interface that declares __toString(), then you'll be free, as a consumer of the library, to use it just as if it returned a plain string.

@thewilkybarkid Unfortunately, that would lead to dirty conditionals to deal with the body, as I mentioned above.

@ghost
ghost added a note

Hello, Neat post. There is a problem along with your website in internet explorer, may test this? IE still is the marketplace leader and a huge portion of folks will miss your fantastic writing because of this problem.
[url=http://www.eurocontrol.es/newsletters/consultoria/catalogo/calzoncillos-de-calvin-klein-baratos.htm]Calzoncillos De Calvin Klein Baratos[/url]

Ah no, I missed last @mtdowling's comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ /**
+ * Gets the body as a string.
+ *
+ * @return string|null Body as a string, or null if not set.
+ */
+ public function getBodyAsString();
+
+ /**
+ * Sets the body.
+ *
+ * The body SHOULD be a string, or an object that implements the
+ * `__toString()` method.
+ *
+ * A null value will remove the existing body.
+ *
+ * An implementation MAY accept other types, but MUST reject anything that
+ * it does not know how to turn into a string.
+ *
+ * @param mixed $body Body.
+ *
+ * @return self Reference to the message.
+ *
+ * @throws InvalidArgumentException When the body is not valid.
+ */
+ public function setBody($body);

It think that to become a standard, the interface must support streamable bodies : you can't just expect an object to be castable to string. Imagine you're transferring a 100MB file in the body, it won't fit in memory, so this must accept a stream as well.
An alternative would be to create a MessageBody interface, that would expose methods such as read() etc.

@staabm
staabm added a note

Good point!

I don't think stream handling should be enforced here, that would stop adoption by lightweight implementers such as Buzz.

Instead, It would be useful to have a StreamInterface defined in a separate PSR (Guzzle and Drupal, for example, have one of these already). Implementers of this interface can then choose to handle StreamInterface objects differently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+}
+```
+
+### 3.2 `Psr\Http\RequestInterface`
+
+```php
+<?php
+
+namespace Psr\Http;
+
+use Psr\Http\Exception\InvalidArgumentException;
+
+/**
+ * A request message from a client to a server.
+ */
+interface RequestInterface extends MessageInterface
+{

Maybe this interface could declare some constants, such as the HTTP verbs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ /**
+ * Gets the method.
+ *
+ * @return string Method.
+ */
+ public function getMethod();
+
+ /**
+ * Sets the method.
+ *
+ * @param string $method Method.
+ *
+ * @return self Reference to the request.
+ */
+ public function setMethod($method);
+
+ /**
+ * Gets the absolute URL.
+ *
+ * @return string URL.
+ */
+ public function getUrl();
+
+ /**
+ * Sets the absolute URL.
+ *
+ * @param string $url URL.
+ *
+ * @return self Reference to the request.
+ *
+ * @throws InvalidArgumentException If the URL is invalid.
+ */
+ public function setUrl($url);
+}
+```
+
+### 3.3 `Psr\Http\ResponseInterface`
+
+```php
+<?php
+
+namespace Psr\Http;
+
+use Psr\Http\Exception\InvalidArgumentException;
+
+/**
+ * A request message from a server to a client.
+ */
+interface ResponseInterface extends MessageInterface
+{
+ /**
+ * Gets the response status code.
+ *
+ * @return integer Status code.
+ */
+ public function getStatusCode();
+
+ /**
+ * Sets the response status code.
+ *
+ * @param integer $statusCode Status code.
@staabm
staabm added a note

getStatusCode returns a string,...

Good spot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ *
+ * @return self Reference to the response.
+ *
+ * @throws InvalidArgumentException When the status code is not valid.
+ */
+ public function setStatusCode($statusCode);
+
+ /**
+ * Gets the response reason phrase.
+ *
+ * If it has not been explicitly set using `setReasonPhrase()` it SHOULD
+ * return the RFC 2616 recommended reason phrase.
@staabm
staabm added a note

Should this PSR contain a class which hold all http status codes and reason phrases?

@Ocramius
Ocramius added a note

Would be VERY useful, but I think it's not really needed, and it would also be hard to have an agreement on it. Interfaces for basic exceptions (4xx, 5xx) may be interesting.

It would be useful to have, but should be in an abstract implementation if anywhere. As for exception interfaces, I think they should be left to implementers if they want them (not sure they add that much, and it would also be very hard to agree on which ones!).

I agree that exceptions should be left to implementations. IMO, there's no need to define them in this PSR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ *
+ * @return string|null Reason phrase, or null if unknown.
+ */
+ public function getReasonPhrase();
+
+ /**
+ * Sets the response reason phrase.
+ *
+ * @param string $reasonPhrase Reason phrase.
+ *
+ * @return self Reference to the response.
+ */
+ public function setReasonPhrase($reasonPhrase);
+}
+```
Something went wrong with that request. Please try again.