Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added PSR-7 Interfaces brief explanations and Basic Usage Guide #79

Merged
merged 4 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ interface that describes a HTTP message. See the specification for more details.
Usage
-----

We'll certainly need some stuff in here.
Before reading the usage guide we recommend reading the PSR-7 interfaces method list:

* [`PSR-7 Interfaces Method List`](docs/PSR7-Interfaces.md)
* [`PSR-7 Usage Guide`](docs/PSR7-Usage.md)
129 changes: 129 additions & 0 deletions docs/PSR7-Interfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#Interfaces

The purpose of this list is to help in finding the methods when working with PSR-7. This can be considered as a cheatsheet for PSR-7 interfaces.

The interfaces defined in PSR-7 are the following:

| Class Name | Description |
|---|---|
| [Psr\Http\Message\MessageInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagemessageinterface) | Representation of a HTTP message |
| [Psr\Http\Message\RequestInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagerequestinterface) | Representation of an outgoing, client-side request. |
| [Psr\Http\Message\ServerRequestInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageserverrequestinterface) | Representation of an incoming, server-side HTTP request. |
| [Psr\Http\Message\ResponseInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageresponseinterface) | Representation of an outgoing, server-side response. |
| [Psr\Http\Message\StreamInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagestreaminterface) | Describes a data stream |
| [Psr\Http\Message\UriInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageuriinterface) | Value object representing a URI. |
| [Psr\Http\Message\UploadedFileInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageuploadedfileinterface) | Value object representing a file uploaded through an HTTP request. |

## `Psr\Http\Message\MessageInterface` Methods

| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `getProtocolVersion()` | Gets HTTP protocol version | 1.0 or 1.1 |
| `withProtocolVersion($version)` | Sets HTTP protocol version | |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be careful about using the term "sets" when discussing the various with*() methods. They return a new instance that incorporates the data you provide, while leaving the original instance unchanged. This is more clear in the request/response-specific sections below, but the verbiage for these general message methods needs to be the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if I write "Sets***" then noting that "***" means returning a new instance with given parameter instead of actually modifying the original HTTP Message (or Request or Response) ?

| `getHeaders()` | Gets all HTTP Headers | [Request Header List](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields), [Response Header List](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields) |
| `hasHeader($name)` | Checks if HTTP Header with given name exists | |
| `getHeader($name)` | Retrieves a array with the values for a single header | |
| `getHeaderLine($name)` | Retrieves a comma-separated string of the values for a single header | |
| `withHeader($name, $value)` | Sets a HTTP Header | If header already exists, value will be overwritten |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Returns a new message instance that includes the provided header; if the header existed in the original instance, replaces the header value from the original message with the value provided when creating the new instance."

| `withAddedHeader($name, $value)` | Appends value to given header | If header already exists value will be appended, if not a new header will be created |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do similar here as with the withHeader() (start with Returns a new message ...).

| `withoutHeader($name)` | Removes HTTP Header with given name| |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Returns a new message instance that does not include the specified HTTP header."

| `getBody()` | Get the HTTP Message Body | Returns object implementing `StreamInterface`|
| `withBody(StreamInterface $body)` | Sets the HTTP Message Body | |


## `Psr\Http\Message\RequestInterface` Methods

Same methods as `Psr\Http\Message\MessageInterface` + the following methods:
| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `getRequestTarget()` | Retrieves the message's request target | origin-form, absolute-form, authority-form, asterisk-form ([RFC7230](https://www.rfc-editor.org/rfc/rfc7230.txt)) |
| `withRequestTarget($requestTarget)` | Return an instance with the specific request-target | |
| `getMethod()` | Retrieves the HTTP method of the request. | GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE (defined in [RFC7231](https://tools.ietf.org/html/rfc7231)), PATCH (defined in [RFC5789](https://tools.ietf.org/html/rfc5789)) |
| `withMethod($method)` | Return an instance with the provided HTTP method | |
| `getUri()` | Retrieves the URI instance | |
| `withUri(UriInterface $uri, $preserveHost = false)` | Returns an instance with the provided URI | |


## `Psr\Http\Message\ServerRequestInterface` Methods

Same methods as `Psr\Http\Message\RequestInterface` + the following methods:

| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `getServerParams() ` | Retrieve server parameters | Typically derived from `$_SERVER` |
| `getCookieParams()` | Retrieves cookies sent by the client to the server. | Typically derived from `$_COOKIES` |
| `withCookieParams(array $cookies)` | Return an instance with the specified cookies | |
| `withQueryParams(array $query)` | Return an instance with the specified query string arguments | |
| `getUploadedFiles()` | Retrieve normalized file upload data | |
| `withUploadedFiles(array $uploadedFiles)` | Create a new instance with the specified uploaded files | |
| `getParsedBody()` | Retrieve any parameters provided in the request body | |
| `withParsedBody($data)` | Return an instance with the specified body parameters | |
| `getAttributes()` | Retrieve attributes derived from the request | |
| `getAttribute($name, $default = null)` | Retrieve a single derived request attribute | |
| `withAttribute($name, $value)` | Return an instance with the specified derived request attribute | |
| `withoutAttribute($name)` | Return an instance that removes the specified derived request attribute | |

## `Psr\Http\Message\ResponseInterface` Methods:

Same methods as `Psr\Http\Message\MessageInterface` + the following methods:

| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `getStatusCode()` | Gets the response status code. | |
| `withStatus($code, $reasonPhrase = '')` | Return an instance with the specified status code and, optionally, reason phrase. | |
| `getReasonPhrase()` | Gets the response reason phrase associated with the status code. | |

## `Psr\Http\Message\StreamInterface` Methods

| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `__toString()` | Reads all data from the stream into a string, from the beginning to end. | |
| `close()` | Closes the stream and any underlying resources. | |
| `detach()` | Separates any underlying resources from the stream. | |
| `getSize()` | Get the size of the stream if known. | |
| `eof()` | Returns true if the stream is at the end of the stream.| |
| `isSeekable()` | Returns whether or not the stream is seekable. | |
| `seek($offset, $whence = SEEK_SET)` | Seek to a position in the stream. | |
| `rewind()` | Seek to the beginning of the stream. | |
| `isWritable()` | Returns whether or not the stream is writable. | |
| `write($string)` | Write data to the stream. | |
| `isReadable()` | Returns whether or not the stream is readable. | |
| `read($length)` | Read data from the stream. | |
| `getContents()` | Returns the remaining contents in a string | |
| `getMetadata($key = null)()` | Get stream metadata as an associative array or retrieve a specific key. | |

## `Psr\Http\Message\UriInterface` Methods

| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `getScheme()` | Retrieve the scheme component of the URI. | |
| `getAuthority()` | Retrieve the authority component of the URI. | |
| `getUserInfo()` | Retrieve the user information component of the URI. | |
| `getHost()` | Retrieve the host component of the URI. | |
| `getPort()` | Retrieve the port component of the URI. | |
| `getPath()` | etrieve the path component of the URI. | |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing R in Retreive.

| `getQuery()` | Retrieve the query string of the URI. | |
| `getFragment()` | Retrieve the fragment component of the URI. | |
| `withScheme($scheme)` | Return an instance with the specified scheme. | |
| `withUserInfo($user, $password = null)` | Return an instance with the specified user information. | |
| `withHost($host)` | Return an instance with the specified host. | |
| `withPort($port)` | Return an instance with the specified port. | |
| `withPath($path)` | Return an instance with the specified path. | |
| `withQuery($query)` | Return an instance with the specified query string. | |
| `withFragment($fragment)` | Return an instance with the specified URI fragment. | |
| `__toString()` | Return the string representation as a URI reference. | |

`Psr\Http\Message\UploadedFileInterface`

| Method Name | Description | Notes |
|------------------------------------| ----------- | ----- |
| `getStream()` | Retrieve a stream representing the uploaded file. | |
| `moveTo($targetPath)` | Move the uploaded file to a new location. | |
| `getSize()` | Retrieve the file size. | |
| `getError()` | Retrieve the error associated with the uploaded file. | |
| `getClientFilename()` | Retrieve the filename sent by the client. | |
| `getClientMediaType()` | Retrieve the media type sent by the client. | |

> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`.
> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered.

168 changes: 168 additions & 0 deletions docs/PSR7-Usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
### PSR-7 Usage

All PSR-7 applications comply with these interfaces
They were created to establish a standard between middleware implementations.

> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`.
> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered.


Enough with the talking, let's put things in practice.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omit this line.

The following examples will illustrate how basic operations are done in PSR-7.

#####Examples (using Zend Diactoros)

Zend Diactoros is an implementation for PSR-7 interfaces. It will be used to illustrate these examples.
Installation guide for Zend Diactoros: [Zend Diactoros Documentation - Installation](https://zendframework.github.io/zend-diactoros/install/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should use Diactoros here, as that sounds like an endorsement of a specific implementation. I'd change this to indicate that instantiation of messages will vary based on implementation, and leave it at that. The rest of the examples will work fine, and should work the same across implementations.


> All other PSR-7 implementations should have the same behaviour.

To use the `Zend Diactoros` classes add this at the beggining of the php file:
```php
<?php

// autoloading

use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Response;

$request = ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
$response = new Response();
```

### Working with HTTP Headers

#### Adding headers to response:

```php
$response->withHeader('My-Custom-Header', 'My Custom Message');
```

#### Appending values to headers

```php
$response->withAddedHeader('My-Custom-Header', 'The second message');
```

#### Checking if header exists:

```php
$request->hasHeader('My-Custom-Header'); // will return false
$response->hasHeader('My-Custom-Header'); // will return true
```

> Note: My-Custom-Header was only added in the Response

#### Getting comma-separated values from a header (also applies to request)

```php
// getting value from request headers
$request->getHeaderLine('Content-Type'); // will return: "text/html; charset=UTF-8"
// getting value from response headers
$response->getHeaderLine('My-Custom-Header'); // will return: "My Custom Message; The second message"
```

#### Getting array of value from a header (also applies to request)
```php
// getting value from request headers
$request->getHeader('Content-Type'); // will return: ["text/html", "charset=UTF-8"]
// getting value from response headers
$response->getHeader('My-Custom-Header'); // will return: ["My Custom Message", "The second message"]
```

#### Removing headers from HTTP Messages
```php
// removing a header from Request, removing deprecated "Content-MD5" header
$request->withoutHeader('Content-MD5');

// removing a header from Response
// effect: the browser won't know the size of the stream
// the browser will download the stream till it ends
$response->withoutHeader('Content-Length');
```

### Working with HTTP Message Body

When working with the PSR-7 there are two methods of implementation:
#### 1. Getting the body separately

> This method makes the body handling easier to understand and is useful when repeatedly calling body methods. (You only call `getBody()` once). Using this method mistakes like `$response->write()` are also prevented.

```php
$body = $response->getBody();
// operations on body, eg. read, write, seek
// ...
// replacing the old body
$response->withBody($body);
// this last statement is optional as we working with objects
// in this case the "new" body is same with the "old" one
// the $body variable has the same value as the one in $request, only the reference is passed
```

#### 2. Working directly on response

> This method is useful when only performing few operations as the `$request->getBody()` statement fragment is required

```php
$response->getBody()->write('hello');
```

### Getting the body contents

The following snippet gets the contents of a stream contents.
> Note: Streams must be rewinded, if content was written into streams, it will be ignored when calling `getContents()` because the stream pointer is set to the last character, which is `\0` - meaning end of stream.
```php
$body = $response->getBody();
$body->rewind(); // or $body->seek(0);
$bodyText = $body->getContends();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/getContends/getContents/

```
> Note: If `$body->seek(1)` is called before `$body->getContents()`, the first character will be ommited as the starting pointer is set to `1`, not `0`. This is why using `$body->rewind()` is recommended.

### Append to body

```php
$response->getBody()->write('Hello'); // writing directly
$body = $request->getBody(); // which is a `StreamInterface`
$body->write('xxxxx');
```

### Prepend to body
Prepending is different when it comes to streams. The content must be copied before writing the content to be prepended.
The following example will explain the behaviour of streams.

```php
// assuming our response is initially empty
$body = $repsonse->getBody();
// writing the string "abcd"
$body->write('abcd');

// seeking to start of stream
$body->seek(0);
// writing 'ef'
$body->write('ef'); // at this point the stream contains "efcd"
```

#### Prepending by rewriting separately

```php
// assuming our response body stream only contains: "abcd"
$body = $response->getBody();
$body->rewind();
$contents = $body->getContents(); // abcd
// seeking the stream to beginning
$body->rewind();
$body->write('ef'); // stream contains "efcd"
$body->write($contents); // stream contains "efabcd"
```

> Note: `getContents()` seeks the stream while reading it, therefore if the second `rewind()` method call was not present the stream would have resulted in `abcdefabcd` because the `write()` method appends to stream if not preceeded by `rewind()` or `seek(0)`.

#### Prepending by using contents as a string
```php
$body = $response->getBody();
$body->rewind();
$contents = $body->getContents(); // efabcd
$contents = 'ef'.$contents;
$body->rewind();
$body->write($contents)
```