From 488c9fc647b57a785f3ff3530bbca7d95d72a6e0 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sun, 4 Jan 2015 08:54:09 -0600 Subject: [PATCH 01/13] Full mutability This patch essentially reverts the interfaces to the original set: - `StreamableInterface` - `MessageInterface` - `RequestInterface` - `ResponseInterface` - `ServerRequestInterface` (originally `IncomingRequestInterface`) Most properties are now mutable, with the exception of server and file parameters in the server request interface (as these are considered non-computable). Additionally, this patch adds a new property to the request interface: the base URL. This property is intended to store the scheme and host, and optionally authentication and port. The "url" is now considered to ONLY store the path + query string. --- src/IncomingResponseInterface.php | 45 ------- src/MessageInterface.php | 65 +++++++++- src/OutgoingRequestInterface.php | 121 ------------------ src/RequestInterface.php | 108 ++++++++++++++++ ...nseInterface.php => ResponseInterface.php} | 59 +-------- ...terface.php => ServerRequestInterface.php} | 99 ++++++++------ 6 files changed, 231 insertions(+), 266 deletions(-) delete mode 100644 src/IncomingResponseInterface.php delete mode 100644 src/OutgoingRequestInterface.php create mode 100644 src/RequestInterface.php rename src/{OutgoingResponseInterface.php => ResponseInterface.php} (54%) rename src/{IncomingRequestInterface.php => ServerRequestInterface.php} (68%) diff --git a/src/IncomingResponseInterface.php b/src/IncomingResponseInterface.php deleted file mode 100644 index 9463351..0000000 --- a/src/IncomingResponseInterface.php +++ /dev/null @@ -1,45 +0,0 @@ - Date: Wed, 7 Jan 2015 10:50:43 -0600 Subject: [PATCH 02/13] Sync with weierophinney/fig-standards#b8f87f4 --- src/MessageInterface.php | 2 +- src/ServerRequestInterface.php | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/MessageInterface.php b/src/MessageInterface.php index 24321bf..fd95b50 100644 --- a/src/MessageInterface.php +++ b/src/MessageInterface.php @@ -126,7 +126,7 @@ public function removeHeader($header); /** * Gets the body of the message. * - * @return StreamableInterface|null Returns the body, or null if not set. + * @return StreamableInterface Returns the body as a stream. */ public function getBody(); diff --git a/src/ServerRequestInterface.php b/src/ServerRequestInterface.php index 9d172bf..8fe33c4 100644 --- a/src/ServerRequestInterface.php +++ b/src/ServerRequestInterface.php @@ -79,6 +79,11 @@ public function setCookieParams(array $cookies); * * Retrieves the deserialized query string arguments, if any. * + * Note: the query params might not be in sync with the URL or server + * params. If you need to ensure you are only getting the original + * values, you may need to parse the composed URL or the `QUERY_STRING` + * composed in the server params. + * * @return array */ public function getQueryParams(); @@ -94,6 +99,9 @@ public function getQueryParams(); * purposes of how duplicate query parameters are handled, and how nested * sets are handled. * + * Setting query string arguments MUST NOT change the URL stored by the + * request, nor the values in the server params. + * * @param array $query Array of query string arguments, typically from * $_GET. * @return void From 9ffea55a78a3960822f28949bcda443eb977b29c Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 7 Jan 2015 16:10:57 -0600 Subject: [PATCH 03/13] Update to weierophinney/fig-standards#af606e5 - Updates to `s/BaseUrl/AbsoluteUri/`. --- src/RequestInterface.php | 49 +++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 481f2be..b38af48 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -41,50 +41,54 @@ public function getMethod(); public function setMethod($method); /** - * Retrieves the base request URL. + * Retrieves the absolute URI. * - * The base URL consists of: + * An absolute URI consists of minimally scheme and host, but can also + * contain: * - * - scheme - * - authentication (if any) - * - server name/host + * - authentication (user/pass) if provided * - port (if non-standard) + * - path (if any) + * - query string (if present) + * - fragment (if present) * - * This method is provided for convenience, particularly when considering - * server-side requests, where data such as the scheme and server name may - * need to be computed from more than one environmental variable. + * If either of the scheme or host are not present, this method MUST return + * null. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @return string Returns the base URL as a string. The URL MUST include - * the scheme and host; if the port is non-standard for the scheme, - * the port MUST be included; authentication data MAY be provided. + * @return string|null Returns the absolute URL as a string. The URL MUST + * include the scheme and host; if the port is non-standard for the + * scheme, the port MUST be included; authentication data MAY be + * provided. If either host or scheme are missing, this method MUST + * return null. */ - public function getBaseUrl(); + public function getAbsoluteUri(); /** - * Sets the base request URL. + * Sets the absolute URI of the request. * - * The base URL MUST be a string, and MUST include the scheme and host. + * The absolute URI MUST be a string, and MUST include the scheme and host. * * If the port is non-standard for the scheme, the port MUST be provided. * * Authentication data MAY be provided. * - * If path, query string, or URL fragment are provided they SHOULD be - * stripped; optionally, an error MAY be raised in such situations. + * Path, query string, and fragment are optional. + * + * When setting the absolute URI, the url (see getUrl() and setUrl()) MUST + * be updated. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @param string $url Base request URL. + * @param string $uri Absolute request URI. * @return void - * @throws \InvalidArgumentException If the URL is invalid. + * @throws \InvalidArgumentException If the URI is invalid. */ - public function setBaseUrl($url); + public function setAbsoluteUri($uri); /** * Retrieves the request URL. * - * The request URL is the same value as REQUEST_URI: the path and query - * string ONLY. + * The request URL is the path and query string ONLY. * * @link http://tools.ietf.org/html/rfc7230#section-5.3 * @return string Returns the URL as a string. The URL MUST be an @@ -99,6 +103,9 @@ public function getUrl(); * string) per RFC 7230 section 5.3; if other URL parts are present, the * method MUST raise an exception OR remove those parts. * + * When setting the URL, the absolute URI (see getAbsoluteUri() and + * setAbsoluteUri()) MUST be updated. + * * @link http://tools.ietf.org/html/rfc7230#section-5.3 * @param string $url Request URL, with path and optionally query string. * @return void From 4ba875b68dd16cbacc45817af2bf88fa7238f0f6 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 12 Jan 2015 07:30:27 -0600 Subject: [PATCH 04/13] Implementing immutability in messages Marked all classes and setters as immutable. All setters have language indicating they MUST be implemented to keep immutable state of the object, and MUST return a new instance with the modified values. --- src/MessageInterface.php | 34 +++++++++++++++++++---- src/RequestInterface.php | 27 ++++++++++++++----- src/ResponseInterface.php | 14 +++++++--- src/ServerRequestInterface.php | 49 +++++++++++++++++++++++++--------- 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/MessageInterface.php b/src/MessageInterface.php index fd95b50..3d66e53 100644 --- a/src/MessageInterface.php +++ b/src/MessageInterface.php @@ -7,6 +7,10 @@ * from a server to a client. This interface defines the methods common to * each. * + * Messages are considered immutable; all methods that might change state MUST + * be implemented such that they retain the internal state of the current + * message and return a new instance that contains the changed state. + * * @link http://www.ietf.org/rfc/rfc7230.txt * @link http://www.ietf.org/rfc/rfc7231.txt */ @@ -27,8 +31,12 @@ public function getProtocolVersion(); * The version string MUST contain only the HTTP version number (e.g., * "1.1", "1.0"). * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new protocol version. + * * @param string $version HTTP protocol version - * @return void + * @return MessageInterface */ public function setProtocolVersion($version); @@ -95,9 +103,13 @@ public function getHeaderLines($header); * The header name is case-insensitive. The header values MUST be a string * or an array of strings. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new and/or updated header and value. + * * @param string $header Header name * @param string|string[] $value Header value(s). - * @return void + * @return MessageInterface * @throws \InvalidArgumentException for invalid header names or values. */ public function setHeader($header, $value); @@ -108,9 +120,13 @@ public function setHeader($header, $value); * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new header and/or value. + * * @param string $header Header name to add * @param string|string[] $value Header value(s). - * @return void + * @return MessageInterface * @throws \InvalidArgumentException for invalid header names or values. */ public function addHeader($header, $value); @@ -118,8 +134,12 @@ public function addHeader($header, $value); /** * Remove a specific header by case-insensitive name. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that removes + * the named header. + * * @param string $header HTTP header to remove - * @return void + * @return MessageInterface */ public function removeHeader($header); @@ -135,8 +155,12 @@ public function getBody(); * * The body MUST be a StreamableInterface object. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * * @param StreamableInterface $body Body. - * @return void + * @return MessageInterface * @throws \InvalidArgumentException When the body is not valid. */ public function setBody(StreamableInterface $body); diff --git a/src/RequestInterface.php b/src/RequestInterface.php index b38af48..6e07b07 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -5,8 +5,8 @@ /** * Representation of an outgoing, client-side request. * - * Per the HTTP specification, this interface includes both accessors for - * and mutators for the following: + * Per the HTTP specification, this interface includes properties for + * each of the following: * * - Protocol version * - HTTP method @@ -14,8 +14,9 @@ * - Headers * - Message body * - * As the request CAN be built iteratively, the interface allows - * mutability of all properties. + * Requests are considered immutable; all methods that might change state MUST + * be implemented such that they retain the internal state of the current + * message and return a new instance that contains the changed state. */ interface RequestInterface extends MessageInterface { @@ -34,8 +35,12 @@ public function getMethod(); * method names are case-sensitive and thus implementations SHOULD NOT * modify the given string. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * changed request method. + * * @param string $method Case-insensitive method. - * @return void + * @return RequestInterface * @throws \InvalidArgumentException for invalid HTTP methods. */ public function setMethod($method); @@ -78,9 +83,13 @@ public function getAbsoluteUri(); * When setting the absolute URI, the url (see getUrl() and setUrl()) MUST * be updated. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * changed URI and updated URL. + * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * @param string $uri Absolute request URI. - * @return void + * @return RequestInterface * @throws \InvalidArgumentException If the URI is invalid. */ public function setAbsoluteUri($uri); @@ -106,9 +115,13 @@ public function getUrl(); * When setting the URL, the absolute URI (see getAbsoluteUri() and * setAbsoluteUri()) MUST be updated. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * changed URL and updated absolute URI. + * * @link http://tools.ietf.org/html/rfc7230#section-5.3 * @param string $url Request URL, with path and optionally query string. - * @return void + * @return RequestInterface * @throws \InvalidArgumentException If the URL is invalid. */ public function setUrl($url); diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php index cfe04e8..f37c0dc 100644 --- a/src/ResponseInterface.php +++ b/src/ResponseInterface.php @@ -5,16 +5,17 @@ /** * Representation of an outgoing, server-side response. * - * Per the HTTP specification, this interface includes both accessors for - * and mutators for the following: + * Per the HTTP specification, this interface includes properties for + * each of the following: * * - Protocol version * - Status code and reason phrase * - Headers * - Message body * - * As the response CAN be built iteratively, the interface allows - * mutability of all properties. + * Responses are considered immutable; all methods that might change state MUST + * be implemented such that they retain the internal state of the current + * message and return a new instance that contains the changed state. */ interface ResponseInterface extends MessageInterface { @@ -35,12 +36,17 @@ public function getStatusCode(); * to the RFC 7231 or IANA recommended reason phrase for the response's * Status-Code. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated status and reason phrase. + * * @link http://tools.ietf.org/html/rfc7231#section-6 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * @param integer $code The 3-digit integer result code to set. * @param null|string $reasonPhrase The reason phrase to use with the * provided status code; if none is provided, implementations MAY * use the defaults as suggested in the HTTP specification. + * @return ResponseInterface * @throws \InvalidArgumentException For invalid status code arguments. */ public function setStatus($code, $reasonPhrase = null); diff --git a/src/ServerRequestInterface.php b/src/ServerRequestInterface.php index 8fe33c4..4b48547 100644 --- a/src/ServerRequestInterface.php +++ b/src/ServerRequestInterface.php @@ -5,8 +5,8 @@ /** * Representation of an incoming, server-side HTTP request. * - * Per the HTTP specification, this interface includes accessors for - * the following: + * Per the HTTP specification, this interface includes properties for + * each of the following: * * - Protocol version * - HTTP method @@ -24,16 +24,21 @@ * - Deserialized body parameters (generally from $_POST) * * $_SERVER and $_FILES values MUST be treated as immutable, as they represent - * application state at the time of request. The other values SHOULD be - * mutable, as they can be restored from $_SERVER, $_FILES, or the request - * body, and may need treatment during the application (e.g., body parameters - * may be deserialized based on content type). + * application state at the time of request; as such, no methods are provided + * to allow modification of those values. The other values provide such methods, + * as they can be restored from $_SERVER, $_FILES, or the request body, and may + * need treatment during the application (e.g., body parameters may be + * deserialized based on content type). * * Additionally, this interface recognizes the utility of introspecting a * request to derive and match additional parameters (e.g., via URI path * matching, decrypting cookie values, deserializing non-form-encoded body * content, matching authorization headers to users, etc). These parameters - * are stored in an "attributes" property, which MUST be mutable. + * are stored in an "attributes" property. + * + * Requests are considered immutable; all methods that might change state MUST + * be implemented such that they retain the internal state of the current + * message and return a new instance that contains the changed state. */ interface ServerRequestInterface extends RequestInterface { @@ -69,8 +74,12 @@ public function getCookieParams(); * be compatible with the structure of $_COOKIE. Typically, this data will * be injected at instantiation. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated cookie values. + * * @param array $cookies Array of key/value pairs representing cookies. - * @return void + * @return ServerRequestInterface */ public function setCookieParams(array $cookies); @@ -102,9 +111,13 @@ public function getQueryParams(); * Setting query string arguments MUST NOT change the URL stored by the * request, nor the values in the server params. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated query string arguments. + * * @param array $query Array of query string arguments, typically from * $_GET. - * @return void + * @return ServerRequestInterface */ public function setQueryParams(array $query); @@ -143,8 +156,12 @@ public function getBodyParams(); * a JSON payload, this method could be used to inject the deserialized * parameters. * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated body parameters. + * * @param array $params The deserialized body parameters. - * @return void + * @return ServerRequestInterface */ public function setBodyParams(array $params); @@ -181,9 +198,13 @@ public function getAttribute($attribute, $default = null); * This method allows setting request attributes, as described in * getAttributes(). * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated attributes. + * * @see getAttributes() * @param array $attributes Attributes derived from the request. - * @return void + * @return ServerRequestInterface */ public function setAttributes(array $attributes); @@ -193,10 +214,14 @@ public function setAttributes(array $attributes); * This method allows setting a single derived request attribute as * described in getAttributes(). * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated attribute. + * * @see getAttributes() * @param string $attribute The attribute name. * @param mixed $value The value of the attribute. - * @return void + * @return ServerRequestInterface */ public function setAttribute($attribute, $value); } From facd2d730194d439e755649a5d3506fd92fe5119 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 14 Jan 2015 17:10:11 -0600 Subject: [PATCH 05/13] Renamed all mutator methods Since messages are immutable, and to avoid confusion with regular setters that change internal state, I've renamed all setter methods to use alternate verbiage. "with" is an accepted standard, and this was used whenever possible, with the following exceptions: - `addHeader` was renamed to `withAddedHeader()`. - `removeHeader` was renamed to `withoutHeader()`. --- src/MessageInterface.php | 25 ++++++++++++++----------- src/RequestInterface.php | 14 +++++++------- src/ResponseInterface.php | 5 +++-- src/ServerRequestInterface.php | 23 +++++++++++------------ 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/MessageInterface.php b/src/MessageInterface.php index 3d66e53..a9c98a7 100644 --- a/src/MessageInterface.php +++ b/src/MessageInterface.php @@ -26,7 +26,7 @@ interface MessageInterface public function getProtocolVersion(); /** - * Set the HTTP protocol version. + * Create a new instance with the specified HTTP protocol version. * * The version string MUST contain only the HTTP version number (e.g., * "1.1", "1.0"). @@ -38,7 +38,7 @@ public function getProtocolVersion(); * @param string $version HTTP protocol version * @return MessageInterface */ - public function setProtocolVersion($version); + public function withProtocolVersion($version); /** * Gets all message headers. @@ -97,8 +97,8 @@ public function getHeader($header); public function getHeaderLines($header); /** - * Sets a header, replacing any existing values of any headers with the - * same case-insensitive name. + * Create a new instance with the provided header, replacing any existing + * values of any headers with the same case-insensitive name. * * The header name is case-insensitive. The header values MUST be a string * or an array of strings. @@ -112,10 +112,11 @@ public function getHeaderLines($header); * @return MessageInterface * @throws \InvalidArgumentException for invalid header names or values. */ - public function setHeader($header, $value); + public function withHeader($header, $value); /** - * Appends a header value for the specified header. + * Creates a new instance, with the specified header appended with the + * given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. @@ -129,10 +130,12 @@ public function setHeader($header, $value); * @return MessageInterface * @throws \InvalidArgumentException for invalid header names or values. */ - public function addHeader($header, $value); + public function withAddedHeader($header, $value); /** - * Remove a specific header by case-insensitive name. + * Creates a new instance, without the specified header. + * + * Header resolution MUST be done without case-insensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that removes @@ -141,7 +144,7 @@ public function addHeader($header, $value); * @param string $header HTTP header to remove * @return MessageInterface */ - public function removeHeader($header); + public function withoutHeader($header); /** * Gets the body of the message. @@ -151,7 +154,7 @@ public function removeHeader($header); public function getBody(); /** - * Sets the body of the message. + * Create a new instance, with the specified message body. * * The body MUST be a StreamableInterface object. * @@ -163,5 +166,5 @@ public function getBody(); * @return MessageInterface * @throws \InvalidArgumentException When the body is not valid. */ - public function setBody(StreamableInterface $body); + public function withBody(StreamableInterface $body); } diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 6e07b07..57a7a5b 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -28,8 +28,8 @@ interface RequestInterface extends MessageInterface public function getMethod(); /** - * Sets the HTTP method to be performed on the resource identified by the - * Request-URI. + * Create a new instance with the provided HTTP method to perform on the + * resource identified by the Request-URI. * * While HTTP method names are typically all uppercase characters, HTTP * method names are case-sensitive and thus implementations SHOULD NOT @@ -43,7 +43,7 @@ public function getMethod(); * @return RequestInterface * @throws \InvalidArgumentException for invalid HTTP methods. */ - public function setMethod($method); + public function withMethod($method); /** * Retrieves the absolute URI. @@ -70,7 +70,7 @@ public function setMethod($method); public function getAbsoluteUri(); /** - * Sets the absolute URI of the request. + * Create a new instance with the provided absolute URI. * * The absolute URI MUST be a string, and MUST include the scheme and host. * @@ -92,7 +92,7 @@ public function getAbsoluteUri(); * @return RequestInterface * @throws \InvalidArgumentException If the URI is invalid. */ - public function setAbsoluteUri($uri); + public function withAbsoluteUri($uri); /** * Retrieves the request URL. @@ -106,7 +106,7 @@ public function setAbsoluteUri($uri); public function getUrl(); /** - * Sets the request URL. + * Create a new instance with the specified request URL. * * The URL MUST be a string. The URL SHOULD be an origin-form (path + query * string) per RFC 7230 section 5.3; if other URL parts are present, the @@ -124,5 +124,5 @@ public function getUrl(); * @return RequestInterface * @throws \InvalidArgumentException If the URL is invalid. */ - public function setUrl($url); + public function withUrl($url); } diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php index f37c0dc..4b5fab9 100644 --- a/src/ResponseInterface.php +++ b/src/ResponseInterface.php @@ -30,7 +30,8 @@ interface ResponseInterface extends MessageInterface public function getStatusCode(); /** - * Sets the status code, and optionally reason phrase, of this response. + * Create a new instance with the specified status code, and optionally + * reason phrase, for the response. * * If no Reason-Phrase is specified, implementations MAY choose to default * to the RFC 7231 or IANA recommended reason phrase for the response's @@ -49,7 +50,7 @@ public function getStatusCode(); * @return ResponseInterface * @throws \InvalidArgumentException For invalid status code arguments. */ - public function setStatus($code, $reasonPhrase = null); + public function withStatus($code, $reasonPhrase = null); /** * Gets the response Reason-Phrase, a short textual description of the Status-Code. diff --git a/src/ServerRequestInterface.php b/src/ServerRequestInterface.php index 4b48547..b023807 100644 --- a/src/ServerRequestInterface.php +++ b/src/ServerRequestInterface.php @@ -66,9 +66,7 @@ public function getServerParams(); public function getCookieParams(); /** - * Set cookies. - * - * Set cookies sent by the client to the server. + * Create a new instance with the specified cookies. * * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST * be compatible with the structure of $_COOKIE. Typically, this data will @@ -81,7 +79,7 @@ public function getCookieParams(); * @param array $cookies Array of key/value pairs representing cookies. * @return ServerRequestInterface */ - public function setCookieParams(array $cookies); + public function withCookieParams(array $cookies); /** * Retrieve query string arguments. @@ -98,7 +96,7 @@ public function setCookieParams(array $cookies); public function getQueryParams(); /** - * Set query string arguments. + * Create a new instance with the specified query string arguments. * * These values SHOULD remain immutable over the course of the incoming * request. They MAY be injected during instantiation, such as from PHP's @@ -119,7 +117,7 @@ public function getQueryParams(); * $_GET. * @return ServerRequestInterface */ - public function setQueryParams(array $query); + public function withQueryParams(array $query); /** * Retrieve the upload file metadata. @@ -146,7 +144,7 @@ public function getFileParams(); public function getBodyParams(); /** - * Set parameters provided in the request body. + * Create a new instance with the specified body parameters. * * These MAY be injected during instantiation from PHP's $_POST * superglobal. The data IS NOT REQUIRED to come from $_POST, but MUST be @@ -163,7 +161,7 @@ public function getBodyParams(); * @param array $params The deserialized body parameters. * @return ServerRequestInterface */ - public function setBodyParams(array $params); + public function withBodyParams(array $params); /** * Retrieve attributes derived from the request. @@ -193,7 +191,8 @@ public function getAttributes(); public function getAttribute($attribute, $default = null); /** - * Set attributes derived from the request. + * Create a new instance with the specified attributes as derived from the + * request. * * This method allows setting request attributes, as described in * getAttributes(). @@ -206,10 +205,10 @@ public function getAttribute($attribute, $default = null); * @param array $attributes Attributes derived from the request. * @return ServerRequestInterface */ - public function setAttributes(array $attributes); + public function withAttributes(array $attributes); /** - * Set a single derived request attribute. + * Create a new instance with the specified derived request attribute. * * This method allows setting a single derived request attribute as * described in getAttributes(). @@ -223,5 +222,5 @@ public function setAttributes(array $attributes); * @param mixed $value The value of the attribute. * @return ServerRequestInterface */ - public function setAttribute($attribute, $value); + public function withAttribute($attribute, $value); } From dd51dcf5edf26743f54ddbdde649cd4277a0e093 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 15 Jan 2015 09:08:18 -0600 Subject: [PATCH 06/13] Created a URI interface - Based on proposals made to the mailing list; essentially, an immutable value object with methods for retrieving each segment of a URI, methods for creating new instances with updated values, a number of "test" methods for determining what type of URI is present, and a method for casting to a string. --- src/UriInterface.php | 262 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 src/UriInterface.php diff --git a/src/UriInterface.php b/src/UriInterface.php new file mode 100644 index 0000000..d4085be --- /dev/null +++ b/src/UriInterface.php @@ -0,0 +1,262 @@ + Date: Thu, 15 Jan 2015 09:10:40 -0600 Subject: [PATCH 07/13] Updated RequestInterface to compose a UriInterface instance --- src/RequestInterface.php | 75 +++++----------------------------------- 1 file changed, 9 insertions(+), 66 deletions(-) diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 57a7a5b..ae820ff 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -10,7 +10,7 @@ * * - Protocol version * - HTTP method - * - URL + * - URI * - Headers * - Message body * @@ -48,81 +48,24 @@ public function withMethod($method); /** * Retrieves the absolute URI. * - * An absolute URI consists of minimally scheme and host, but can also - * contain: - * - * - authentication (user/pass) if provided - * - port (if non-standard) - * - path (if any) - * - query string (if present) - * - fragment (if present) - * - * If either of the scheme or host are not present, this method MUST return - * null. + * This method MUST return a UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @return string|null Returns the absolute URL as a string. The URL MUST - * include the scheme and host; if the port is non-standard for the - * scheme, the port MUST be included; authentication data MAY be - * provided. If either host or scheme are missing, this method MUST - * return null. + * @return UriInterface Returns a UriInterface instance representing the + * URI of the request, if any. */ - public function getAbsoluteUri(); + public function getUri(); /** - * Create a new instance with the provided absolute URI. - * - * The absolute URI MUST be a string, and MUST include the scheme and host. - * - * If the port is non-standard for the scheme, the port MUST be provided. - * - * Authentication data MAY be provided. - * - * Path, query string, and fragment are optional. - * - * When setting the absolute URI, the url (see getUrl() and setUrl()) MUST - * be updated. + * Create a new instance with the provided URI. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the - * changed URI and updated URL. + * new UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @param string $uri Absolute request URI. - * @return RequestInterface - * @throws \InvalidArgumentException If the URI is invalid. - */ - public function withAbsoluteUri($uri); - - /** - * Retrieves the request URL. - * - * The request URL is the path and query string ONLY. - * - * @link http://tools.ietf.org/html/rfc7230#section-5.3 - * @return string Returns the URL as a string. The URL MUST be an - * origin-form (path + query string), per RFC 7230 section 5.3 - */ - public function getUrl(); - - /** - * Create a new instance with the specified request URL. - * - * The URL MUST be a string. The URL SHOULD be an origin-form (path + query - * string) per RFC 7230 section 5.3; if other URL parts are present, the - * method MUST raise an exception OR remove those parts. - * - * When setting the URL, the absolute URI (see getAbsoluteUri() and - * setAbsoluteUri()) MUST be updated. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return a new instance that has the - * changed URL and updated absolute URI. - * - * @link http://tools.ietf.org/html/rfc7230#section-5.3 - * @param string $url Request URL, with path and optionally query string. + * @param UriInterface $uri New request URI to use. * @return RequestInterface - * @throws \InvalidArgumentException If the URL is invalid. */ - public function withUrl($url); + public function withUri(UriInterface $uri); } From 77ba53d7284565914c062abc285b281fe7facaf4 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 15 Jan 2015 15:11:06 -0600 Subject: [PATCH 08/13] Added a rewind() definition - per discussion with @Crell --- src/StreamableInterface.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/StreamableInterface.php b/src/StreamableInterface.php index 96fad9d..43ce381 100644 --- a/src/StreamableInterface.php +++ b/src/StreamableInterface.php @@ -83,6 +83,17 @@ public function isSeekable(); */ public function seek($offset, $whence = SEEK_SET); + /** + * Seek to the beginning of the stream. + * + * If the stream is not seekable, this method will return FALSE, indicating + * failure; otherwise, it will perform a seek(0), and return the status of + * that operation. + * + * @return bool Returns TRUE on success or FALSE on failure. + */ + public function rewind(); + /** * Returns whether or not the stream is writable. * From 754d423919369e204d2961548e4192d5e47939b5 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 15 Jan 2015 16:20:09 -0600 Subject: [PATCH 09/13] Normalized to follow original URI proposal See https://github.com/php-fig/fig-standards/blob/3486dc9184e9a099118389cd3e032295852d011e/proposed/uri.md I examined this proposal, as well as looked in depth at RFC's 3986 and 7230 to see what overlap we had and/or needed. The interface in its current form now is mostly compatible with the original proposal. It differs in the following ways: - Since this proposal targets HTTP requests specifically, I did not separate URI vs Hierarchical URI vs Opaque. - This proposal combines aspects of the originally proposed UriInterface and the HierarchicalUriInterface, particularly around the various getters. - I have omitted the `to(Encoded|Decoded)String()` methods, as they are essentially irrelevant for HTTP messages (requests will always want decoded URIs). - I have omitted `getQueryAsArray()` as I'm not 100% sold on their efficacy for HTTP messages, particularly as ServerRequest already defines `getQueryParams()` - I omitted `getHierarchicalPart()`, as I could not find a use case within the domain of HTTP messages. - I omitted `normalize()`, `resolve()`, and `relativize()` as I did not have immediate use cases for them with regards to HTTP messages. They may be useful for HTTP clients, however. - I modified `getAuthority()` to return the authority segment of a URI. - I added `getUserInfo()` to perform what `getAuthority()` was doing previously, and renamed `withAuthority()` to `withUserInfo()`. With the changes made, an implementation that bridges the original interfaces and those in this proposal can be made that are 100% compatible. --- src/UriInterface.php | 56 +++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/UriInterface.php b/src/UriInterface.php index d4085be..a448cf8 100644 --- a/src/UriInterface.php +++ b/src/UriInterface.php @@ -27,23 +27,37 @@ public function getScheme(); /** * Retrieve the authority portion of the URI. * - * The authority portion of a URI when present, can consist of a username, - * and optionally the password/credentials for that user. The return MUST - * be a string, in the format of "username[:password]", where the colon and - * password are only present if they were provided. (Brackets MUST NOT be - * present; they are used here to indicate that those items are optional) + * The authority portion of the URI is: * - * The string returned MUST strip off the trailing "@" delimiter if - * present. + *
+     * [user-info@]host[:port]
+     * 
+ * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. * * This method MUST return an empty string if no authority information is * present. * - * @return string Authority portion of the URI, in "username[:password]" + * @return string Authority portion of the URI, in "[user-info@]host[:port]" * format. */ public function getAuthority(); + /** + * Retrieve the user information portion of the URI, if present. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * Implementations MUST NOT return the "@" suffix when returning this value. + * + * @return string User information portion of the URI, if present, in + * "username[:password]" format. + */ + public function getUserInfo(); + /** * Retrieve the host segment of the URI. * @@ -119,20 +133,20 @@ public function getFragment(); public function withScheme($scheme); /** - * Create a new instance with the specified authority information. + * Create a new instance with the specified user information. * * This method MUST retain the state of the current instance, and return - * a new instance that contains the specified authority information. + * a new instance that contains the specified user information. * - * Password is optional, but the authority information MUST include the + * Password is optional, but the user information MUST include the * user. * * @param string $user User name to use for authority. * @param null|string $password Password associated with $user. - * @return UriInterface A new instance with the specified authority + * @return UriInterface A new instance with the specified user * information. */ - public function withAuthority($user, $password = null); + public function withUserInfo($user, $password = null); /** * Create a new instance with the specified host. @@ -208,18 +222,20 @@ public function withFragment($fragment); * Origin-form is a URI that includes only the path and optionally the * query string. * + * @see http://tools.ietf.org/html/rfc7230#section-5.3.1 * @return bool */ - public function isOriginForm(); + public function isOrigin(); /** * Indicate whether the URI is absolute. * * An absolute URI contains minimally the scheme and host. * + * @see http://tools.ietf.org/html/rfc7230#section-5.3.2 * @return bool */ - public function isAbsoluteForm(); + public function isAbsolute(); /** * Indicate whether the URI is in authority form. @@ -227,18 +243,20 @@ public function isAbsoluteForm(); * An authority-form URI is an absolute URI that also contains authority * information. * + * @see http://tools.ietf.org/html/rfc7230#section-5.3.3 * @return bool */ - public function isAuthorityForm(); + public function isAuthority(); /** - * Indicate whether the URI is an asterix form. + * Indicate whether the URI is an asterisk-form. * - * An asterix form URI will have "*" as the path, and no other URI parts. + * An asterisk form URI will have "*" as the path, and no other URI parts. * + * @see http://tools.ietf.org/html/rfc7230#section-5.3.4 * @return bool */ - public function isAsterixForm(); + public function isAsterisk(); /** * Return the string representation of the URI. From 604bc8a1d1790f15d8cdf7de92733fc8daca79a4 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 16 Jan 2015 11:44:35 -0600 Subject: [PATCH 10/13] Use consistent verbiage and formatting. Cleaned up for consistent verbiage around mutability. All return values that returned the interface name were altered to use `self`, to ensure that they report correctly in documentation and IDEs. `ServerRequest::setAttributes()` was removed, per @Crell, and `ServerRequest::withoutAttribute()` added (to make usage consistent with headers). --- src/MessageInterface.php | 22 +++++---- src/RequestInterface.php | 9 ++-- src/ResponseInterface.php | 2 +- src/ServerRequestInterface.php | 44 +++++++++-------- src/StreamableInterface.php | 28 +++++------ src/UriInterface.php | 88 +++++++++++++++++++++------------- 6 files changed, 110 insertions(+), 83 deletions(-) diff --git a/src/MessageInterface.php b/src/MessageInterface.php index a9c98a7..23360b7 100644 --- a/src/MessageInterface.php +++ b/src/MessageInterface.php @@ -17,7 +17,7 @@ interface MessageInterface { /** - * Gets the HTTP protocol version as a string. + * Retrieves the HTTP protocol version as a string. * * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). * @@ -36,12 +36,12 @@ public function getProtocolVersion(); * new protocol version. * * @param string $version HTTP protocol version - * @return MessageInterface + * @return self */ public function withProtocolVersion($version); /** - * Gets all message headers. + * Retrieves all message headers. * * The keys represent the header name as it will be sent over the wire, and * each value is an array of strings associated with the header. @@ -81,7 +81,8 @@ public function hasHeader($header); * a comma. * * NOTE: Not all header values may be appropriately represented using - * comma concatenation. + * comma concatenation. For such headers, use getHeaderLines() instead + * and supply your own delimiter when concatenating. * * @param string $header Case-insensitive header name. * @return string @@ -109,7 +110,7 @@ public function getHeaderLines($header); * * @param string $header Header name * @param string|string[] $value Header value(s). - * @return MessageInterface + * @return self * @throws \InvalidArgumentException for invalid header names or values. */ public function withHeader($header, $value); @@ -119,7 +120,8 @@ public function withHeader($header, $value); * given value. * * Existing values for the specified header will be maintained. The new - * value(s) will be appended to the existing list. + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the @@ -127,7 +129,7 @@ public function withHeader($header, $value); * * @param string $header Header name to add * @param string|string[] $value Header value(s). - * @return MessageInterface + * @return self * @throws \InvalidArgumentException for invalid header names or values. */ public function withAddedHeader($header, $value); @@ -135,14 +137,14 @@ public function withAddedHeader($header, $value); /** * Creates a new instance, without the specified header. * - * Header resolution MUST be done without case-insensitivity. + * Header resolution MUST be done without case-sensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that removes * the named header. * * @param string $header HTTP header to remove - * @return MessageInterface + * @return self */ public function withoutHeader($header); @@ -163,7 +165,7 @@ public function getBody(); * new body stream. * * @param StreamableInterface $body Body. - * @return MessageInterface + * @return self * @throws \InvalidArgumentException When the body is not valid. */ public function withBody(StreamableInterface $body); diff --git a/src/RequestInterface.php b/src/RequestInterface.php index ae820ff..ca79ff5 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -28,8 +28,7 @@ interface RequestInterface extends MessageInterface public function getMethod(); /** - * Create a new instance with the provided HTTP method to perform on the - * resource identified by the Request-URI. + * Create a new instance with the provided HTTP method. * * While HTTP method names are typically all uppercase characters, HTTP * method names are case-sensitive and thus implementations SHOULD NOT @@ -40,13 +39,13 @@ public function getMethod(); * changed request method. * * @param string $method Case-insensitive method. - * @return RequestInterface + * @return self * @throws \InvalidArgumentException for invalid HTTP methods. */ public function withMethod($method); /** - * Retrieves the absolute URI. + * Retrieves the URI instance. * * This method MUST return a UriInterface instance. * @@ -65,7 +64,7 @@ public function getUri(); * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * @param UriInterface $uri New request URI to use. - * @return RequestInterface + * @return self */ public function withUri(UriInterface $uri); } diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php index 4b5fab9..a9f0fe1 100644 --- a/src/ResponseInterface.php +++ b/src/ResponseInterface.php @@ -47,7 +47,7 @@ public function getStatusCode(); * @param null|string $reasonPhrase The reason phrase to use with the * provided status code; if none is provided, implementations MAY * use the defaults as suggested in the HTTP specification. - * @return ResponseInterface + * @return self * @throws \InvalidArgumentException For invalid status code arguments. */ public function withStatus($code, $reasonPhrase = null); diff --git a/src/ServerRequestInterface.php b/src/ServerRequestInterface.php index b023807..cbccd3e 100644 --- a/src/ServerRequestInterface.php +++ b/src/ServerRequestInterface.php @@ -10,12 +10,12 @@ * * - Protocol version * - HTTP method - * - URL + * - URI * - Headers * - Message body * * Additionally, it encapsulates all data as it has arrived to the - * application from the PHP environment, including: + * application from the CGI and/or PHP environment, including: * * - The values represented in $_SERVER. * - Any cookies provided (generally via $_COOKIE) @@ -77,7 +77,7 @@ public function getCookieParams(); * updated cookie values. * * @param array $cookies Array of key/value pairs representing cookies. - * @return ServerRequestInterface + * @return self */ public function withCookieParams(array $cookies); @@ -102,7 +102,7 @@ public function getQueryParams(); * request. They MAY be injected during instantiation, such as from PHP's * $_GET superglobal, or MAY be derived from some other value such as the * URI. In cases where the arguments are parsed from the URI, the data - * MUST be compatible with what PHP's `parse_str()` would return for + * MUST be compatible with what PHP's parse_str() would return for * purposes of how duplicate query parameters are handled, and how nested * sets are handled. * @@ -115,7 +115,7 @@ public function getQueryParams(); * * @param array $query Array of query string arguments, typically from * $_GET. - * @return ServerRequestInterface + * @return self */ public function withQueryParams(array $query); @@ -159,7 +159,7 @@ public function getBodyParams(); * updated body parameters. * * @param array $params The deserialized body parameters. - * @return ServerRequestInterface + * @return self */ public function withBodyParams(array $params); @@ -183,6 +183,9 @@ public function getAttributes(); * getAttributes(). If the attribute has not been previously set, returns * the default value as provided. * + * This method obviates the need for a hasAttribute() method, as it allows + * specifying a default value to return if the attribute is not found. + * * @see getAttributes() * @param string $attribute Attribute name. * @param mixed $default Default value to return if the attribute does not exist. @@ -191,36 +194,37 @@ public function getAttributes(); public function getAttribute($attribute, $default = null); /** - * Create a new instance with the specified attributes as derived from the - * request. + * Create a new instance with the specified derived request attribute. * - * This method allows setting request attributes, as described in - * getAttributes(). + * This method allows setting a single derived request attribute as + * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the - * updated attributes. + * updated attribute. * * @see getAttributes() - * @param array $attributes Attributes derived from the request. - * @return ServerRequestInterface + * @param string $attribute The attribute name. + * @param mixed $value The value of the attribute. + * @return self */ - public function withAttributes(array $attributes); + public function withAttribute($attribute, $value); /** - * Create a new instance with the specified derived request attribute. + * Create a new instance that removes the specified derived request + * attribute. * - * This method allows setting a single derived request attribute as + * This method allows removing a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return a new instance that has the - * updated attribute. + * immutability of the message, and MUST return a new instance that removes + * the attribute. * * @see getAttributes() * @param string $attribute The attribute name. * @param mixed $value The value of the attribute. - * @return ServerRequestInterface + * @return self */ - public function withAttribute($attribute, $value); + public function withoutAttribute($attribute, $value); } diff --git a/src/StreamableInterface.php b/src/StreamableInterface.php index 43ce381..8be1fbc 100644 --- a/src/StreamableInterface.php +++ b/src/StreamableInterface.php @@ -70,15 +70,13 @@ public function isSeekable(); /** * Seek to a position in the stream. * - * @link http://www.php.net/manual/en/function.fseek.php + * @link http://www.php.net/manual/en/function.fseek.php * @param int $offset Stream offset * @param int $whence Specifies how the cursor position will be calculated - * based on the seek offset. Valid values are identical - * to the built-in PHP $whence values for `fseek()`. - * SEEK_SET: Set position equal to offset bytes - * SEEK_CUR: Set position to current location plus offset - * SEEK_END: Set position to end-of-stream plus offset - * + * based on the seek offset. Valid values are identical to the built-in + * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to + * offset bytes SEEK_CUR: Set position to current location plus offset + * SEEK_END: Set position to end-of-stream plus offset. * @return bool Returns TRUE on success or FALSE on failure. */ public function seek($offset, $whence = SEEK_SET); @@ -90,6 +88,8 @@ public function seek($offset, $whence = SEEK_SET); * failure; otherwise, it will perform a seek(0), and return the status of * that operation. * + * @see seek() + * @link http://www.php.net/manual/en/function.fseek.php * @return bool Returns TRUE on success or FALSE on failure. */ public function rewind(); @@ -105,9 +105,8 @@ public function isWritable(); * Write data to the stream. * * @param string $string The string that is to be written. - * * @return int|bool Returns the number of bytes written to the stream on - * success or FALSE on failure. + * success or FALSE on failure. */ public function write($string); @@ -122,10 +121,10 @@ public function isReadable(); * Read data from the stream. * * @param int $length Read up to $length bytes from the object and return - * them. Fewer than $length bytes may be returned if - * underlying stream call returns fewer bytes. + * them. Fewer than $length bytes may be returned if underlying stream + * call returns fewer bytes. * @return string|false Returns the data read from the stream, false if - * unable to read or if an error occurs. + * unable to read or if an error occurs. */ public function read($length); @@ -145,9 +144,8 @@ public function getContents(); * @link http://php.net/manual/en/function.stream-get-meta-data.php * @param string $key Specific metadata to retrieve. * @return array|mixed|null Returns an associative array if no key is - * provided. Returns a specific key value if a key - * is provided and the value is found, or null if - * the key is not found. + * provided. Returns a specific key value if a key is provided and the + * value is found, or null if the key is not found. */ public function getMetadata($key = null); } diff --git a/src/UriInterface.php b/src/UriInterface.php index a448cf8..b408185 100644 --- a/src/UriInterface.php +++ b/src/UriInterface.php @@ -4,21 +4,24 @@ /** * Value object representing a URI. * - * @see http://tools.ietf.org/html/rfc3986 (the URI specification) - * @see http://tools.ietf.org/html/rfc7230#section-2.7 (URIs as used in the HTTP specification) + * URIs are considered immutable; all methods that might change state MUST + * be implemented such that they retain the internal state of the current + * instance and return a new instance that contains the changed state. + * + * @link http://tools.ietf.org/html/rfc3986 (the URI specification) + * @link http://tools.ietf.org/html/rfc7230#section-2.7 (URIs as used in the HTTP specification) */ interface UriInterface { /** * Retrieve the URI scheme. * - * Generally this will be one of "http" or "https", but implementations may - * allow for other schemes when desired. + * Implementations SHOULD restrict values to "http", "https", or an empty + * string but MAY accommodate other schemes if required. * * If no scheme is present, this method MUST return an empty string. * - * The string returned MUST strip off the "://" trailing delimiter if - * present. + * The string returned MUST omit the trailing "://" delimiter if present. * * @return string The scheme of the URI. */ @@ -79,7 +82,7 @@ public function getHost(); * a null value. * * If no port is present, but a scheme is present, this method MAY return - * the standard port for that scheme. + * the standard port for that scheme, but SHOULD return null. * * @return null|int The port for the URI. */ @@ -101,7 +104,7 @@ public function getPath(); * This method MUST return a string; if no query string is present, it MUST * return an empty string. * - * The string returned MUST strip off any leading "?" character. + * The string returned MUST omit the leading "?" character. * * @return string The URI query string. */ @@ -113,7 +116,7 @@ public function getQuery(); * This method MUST return a string; if no fragment is present, it MUST * return an empty string. * - * The string returned MUST strip off any leading "#" character. + * The string returned MUST omit the leading "#" character. * * @return string The URI fragment. */ @@ -126,8 +129,13 @@ public function getFragment(); * a new instance that contains the specified scheme. If the scheme * provided includes the "://" delimiter, it MUST be removed. * + * Implementations SHOULD restrict values to "http", "https", or an empty + * string but MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * * @param string $scheme The scheme to use with the new instance. - * @return UriInterface A new instance with the specified scheme. + * @return self A new instance with the specified scheme. * @throws \InvalidArgumentException for invalid or unsupported schemes. */ public function withScheme($scheme); @@ -139,12 +147,12 @@ public function withScheme($scheme); * a new instance that contains the specified user information. * * Password is optional, but the user information MUST include the - * user. + * user; an empty string for the user is equivalent to removing user + * information. * * @param string $user User name to use for authority. * @param null|string $password Password associated with $user. - * @return UriInterface A new instance with the specified user - * information. + * @return self A new instance with the specified user information. */ public function withUserInfo($user, $password = null); @@ -154,8 +162,10 @@ public function withUserInfo($user, $password = null); * This method MUST retain the state of the current instance, and return * a new instance that contains the specified host. * + * An empty host value is equivalent to removing the host. + * * @param string $host Hostname to use with the new instance. - * @return UriInterface A new instance with the specified host. + * @return self A new instance with the specified host. * @throws \InvalidArgumentException for invalid hostnames. */ public function withHost($host); @@ -166,8 +176,15 @@ public function withHost($host); * This method MUST retain the state of the current instance, and return * a new instance that contains the specified port. * - * @param int $port Port to use with the new instance. - * @return UriInterface A new instance with the specified port. + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port Port to use with the new instance; a null value + * removes the port information. + * @return self A new instance with the specified port. * @throws \InvalidArgumentException for invalid ports. */ public function withPort($port); @@ -181,8 +198,10 @@ public function withPort($port); * The path MUST be prefixed with "/"; if not, the implementation MAY * provide the prefix itself. * + * An empty path value is equivalent to removing the path. + * * @param string $path The path to use with the new instance. - * @return UriInterface A new instance with the specified path. + * @return self A new instance with the specified path. * @throws \InvalidArgumentException for invalid paths. */ public function withPath($path); @@ -197,8 +216,10 @@ public function withPath($path); * Additionally, the query string SHOULD be parseable by parse_str() in * order to be valid. * + * An empty query string value is equivalent to removing the query string. + * * @param string $query The query string to use with the new instance. - * @return UriInterface A new instance with the specified query string. + * @return self A new instance with the specified query string. * @throws \InvalidArgumentException for invalid query strings. */ public function withQuery($query); @@ -211,18 +232,20 @@ public function withQuery($query); * * If the fragment is prefixed by "#", that character MUST be removed. * + * An empty fragment value is equivalent to removing the fragment. + * * @param string $fragment The URI fragment to use with the new instance. - * @return UriInterface A new instance with the specified URI fragment. + * @return self A new instance with the specified URI fragment. */ public function withFragment($fragment); /** * Indicate whether the URI is in origin-form. * - * Origin-form is a URI that includes only the path and optionally the + * Origin-form is a URI that includes only the path, and optionally the * query string. * - * @see http://tools.ietf.org/html/rfc7230#section-5.3.1 + * @link http://tools.ietf.org/html/rfc7230#section-5.3.1 * @return bool */ public function isOrigin(); @@ -230,9 +253,11 @@ public function isOrigin(); /** * Indicate whether the URI is absolute. * - * An absolute URI contains minimally the scheme and host. + * An absolute URI contains minimally a non-empty scheme and non-empty + * authority. * - * @see http://tools.ietf.org/html/rfc7230#section-5.3.2 + * @see getAuthority() + * @link http://tools.ietf.org/html/rfc7230#section-5.3.2 * @return bool */ public function isAbsolute(); @@ -240,10 +265,11 @@ public function isAbsolute(); /** * Indicate whether the URI is in authority form. * - * An authority-form URI is an absolute URI that also contains authority + * An authority-form URI is an URI that contains ONLY the authority * information. * - * @see http://tools.ietf.org/html/rfc7230#section-5.3.3 + * @see getAuthority() + * @link http://tools.ietf.org/html/rfc7230#section-5.3.3 * @return bool */ public function isAuthority(); @@ -251,9 +277,10 @@ public function isAuthority(); /** * Indicate whether the URI is an asterisk-form. * - * An asterisk form URI will have "*" as the path, and no other URI parts. + * An asterisk form URI will contain "*" as the path, and no other URI + * segments. * - * @see http://tools.ietf.org/html/rfc7230#section-5.3.4 + * @link http://tools.ietf.org/html/rfc7230#section-5.3.4 * @return bool */ public function isAsterisk(); @@ -265,11 +292,8 @@ public function isAsterisk(); * delimiters: * * - If a scheme is present, "://" MUST append the value. - * - If authority information is present, the password MUST be separated - * from the user by a ":", and the full authority string MUST be appended - * with an "@" character. - * - If a port is present, and non-standard for the given scheme, it MUST - * be provided, and MUST be prefixed by a ":" character. + * - If the authority information is present, that value will be + * contatenated. * - If a path is present, it MUST be prefixed by a "/" character. * - If a query string is present, it MUST be prefixed by a "?" character. * - If a URI fragment is present, it MUST be prefixed by a "#" character. From 07ba2d551a05d11ad6b12d94da5e29d286c0b657 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 17 Jan 2015 10:16:21 -0600 Subject: [PATCH 11/13] Provide more and better description of the interface Indicated that this interface _typically_ represents a URI, but technically represents a request target per 7230. --- src/UriInterface.php | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/UriInterface.php b/src/UriInterface.php index b408185..aa4e92f 100644 --- a/src/UriInterface.php +++ b/src/UriInterface.php @@ -2,11 +2,31 @@ namespace Psr\Http\Message; /** - * Value object representing a URI. + * Value object representing the request target, and typically a URI. * - * URIs are considered immutable; all methods that might change state MUST - * be implemented such that they retain the internal state of the current - * instance and return a new instance that contains the changed state. + * Instances of this interface are considered immutable; all methods that + * might change state MUST be implemented such that they retain the internal + * state of the current instance and return a new instance that contains the + * changed state. + * + * Since this interface represents a request target per RFC 7230, the instance + * can potentially omit the scheme and authority. As such, test methods exist + * for determining what request target form is in use: + * + * - isOrigin() tests if the target is in origin-form (path + optional query + * string only). + * - isAbsolute() tests if the target is in absolute-form (minimally scheme + + * authority). + * - isAuthority() tests if the target contains the authority only. + * - isAsterisk() tests if the entirety of the target is '*'. + * + * These target forms are included, as they are valid forms for use with an + * HTTP request, and will appear without other URI segments available within + * the request line. This interface models the target as it appears. + * + * Typically, for all forms other than absolute-form, minimally the Host header + * will be also be present in the request message. For server-side requests, + * the scheme will typically be discoverable in the server parameters. * * @link http://tools.ietf.org/html/rfc3986 (the URI specification) * @link http://tools.ietf.org/html/rfc7230#section-2.7 (URIs as used in the HTTP specification) From c898a76afdf8d86ace023dbf853cbdf64aa76b0c Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 17 Jan 2015 10:28:05 -0600 Subject: [PATCH 12/13] $value is not necessary to withoutAttribute --- src/ServerRequestInterface.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ServerRequestInterface.php b/src/ServerRequestInterface.php index cbccd3e..6a58a0a 100644 --- a/src/ServerRequestInterface.php +++ b/src/ServerRequestInterface.php @@ -223,8 +223,7 @@ public function withAttribute($attribute, $value); * * @see getAttributes() * @param string $attribute The attribute name. - * @param mixed $value The value of the attribute. * @return self */ - public function withoutAttribute($attribute, $value); + public function withoutAttribute($attribute); } From 2721ad142ba72dbb071e94db5cb3cff897cefc06 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 17 Jan 2015 12:19:07 -0600 Subject: [PATCH 13/13] Rename URI interface for clarity While it's generally a URI, for the purposes of PSR-7, it technically represents the request target. As such, UriTargetInterface will be more clear as to its purpose, and stave off questions of "why is a relative URL considered valid?" --- src/RequestInterface.php | 12 ++++++------ ...{UriInterface.php => UriTargetInterface.php} | 17 ++++++++++------- 2 files changed, 16 insertions(+), 13 deletions(-) rename src/{UriInterface.php => UriTargetInterface.php} (96%) diff --git a/src/RequestInterface.php b/src/RequestInterface.php index ca79ff5..54ec043 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -47,11 +47,11 @@ public function withMethod($method); /** * Retrieves the URI instance. * - * This method MUST return a UriInterface instance. + * This method MUST return a UriTargetInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @return UriInterface Returns a UriInterface instance representing the - * URI of the request, if any. + * @return UriTargetInterface Returns a UriTargetInterface instance + * representing the URI of the request, if any. */ public function getUri(); @@ -60,11 +60,11 @@ public function getUri(); * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the - * new UriInterface instance. + * new UriTargetInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @param UriInterface $uri New request URI to use. + * @param UriTargetInterface $uri New request URI to use. * @return self */ - public function withUri(UriInterface $uri); + public function withUri(UriTargetInterface $uri); } diff --git a/src/UriInterface.php b/src/UriTargetInterface.php similarity index 96% rename from src/UriInterface.php rename to src/UriTargetInterface.php index aa4e92f..02d309b 100644 --- a/src/UriInterface.php +++ b/src/UriTargetInterface.php @@ -4,25 +4,28 @@ /** * Value object representing the request target, and typically a URI. * - * Instances of this interface are considered immutable; all methods that + * Instances of this interface are considered immutable; all methods that * might change state MUST be implemented such that they retain the internal * state of the current instance and return a new instance that contains the * changed state. * * Since this interface represents a request target per RFC 7230, the instance - * can potentially omit the scheme and authority. As such, test methods exist - * for determining what request target form is in use: + * MAY represent an absolute URI OR one of the request targets that are not + * fully qualified URIs, including origin-form, authority-form, or + * asterisk-form. As such, test methods exist for determining what request + * target form is in use: * - * - isOrigin() tests if the target is in origin-form (path + optional query - * string only). * - isAbsolute() tests if the target is in absolute-form (minimally scheme + * authority). + * - isOrigin() tests if the target is in origin-form (path + optional query + * string only). * - isAuthority() tests if the target contains the authority only. * - isAsterisk() tests if the entirety of the target is '*'. * * These target forms are included, as they are valid forms for use with an * HTTP request, and will appear without other URI segments available within - * the request line. This interface models the target as it appears. + * the request line. This interface models the target as it appears in the + * incoming request line or as it will be emitted by a client. * * Typically, for all forms other than absolute-form, minimally the Host header * will be also be present in the request message. For server-side requests, @@ -31,7 +34,7 @@ * @link http://tools.ietf.org/html/rfc3986 (the URI specification) * @link http://tools.ietf.org/html/rfc7230#section-2.7 (URIs as used in the HTTP specification) */ -interface UriInterface +interface UriTargetInterface { /** * Retrieve the URI scheme.