Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 7af500a

Browse files
committed
Documented #58 and #52
1 parent 7ad6914 commit 7af500a

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

doc/book/custom-responses.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Custom Responses
2+
3+
When developing server-side applications, the message type you're most likely to create manually is
4+
the response. In such cases, the standard signature can be an obstacle to usability. Let's review:
5+
6+
```php
7+
class Response implements ResponseInterface
8+
{
9+
public function __construct($body = 'php://temp', $status = 200, array $headers = []);
10+
}
11+
```
12+
13+
Some standard use cases, however, make this un-wieldy:
14+
15+
- Returning a response containing HTML; in this case, you likely want to provide the HTML to the
16+
constructor, not a stream with the HTML injected.
17+
- Returning a response containing JSON; in this case, you likely want to provide the data to
18+
seriazlize to JSON, not a stream containing serialized JSON.
19+
- Returning a response with no content; in this case, you don't want to bother with the body at all.
20+
21+
Starting with version 1.1, Diactoros offers several custom response types and factories for
22+
simplifying these common tasks.
23+
24+
## String responses
25+
26+
`Zend\Diactoros\Response\StringResponse` provides factory methods for two standard string response
27+
types: HTML and JSON.
28+
29+
### HTML
30+
31+
The `html()` factory will create a response with the provided HTML as a payload, setting the
32+
`Content-Type` header to `text/html` by default:
33+
34+
```php
35+
$response = StringResponse::html($htmlContent);
36+
```
37+
38+
The factory allows passing two additional arguments: a status code, and an array of headers. These
39+
allow you to further seed the initial state of the response.
40+
41+
Headers must be in the same format as you would provide to the
42+
[Response constructor][api.md#response-message].
43+
44+
### JSON
45+
The `json()` factory accepts a data structure to convert to JSON, and returns a response with the
46+
JSON content and the `Content-Type` header set to `application/json`:
47+
48+
```php
49+
$response = StringResponse::json($data);
50+
```
51+
52+
If a null value is provide, an empty JSON object is used for the content. Scalar data is cast to an
53+
array before serialization. If providing an object, we recommend implementing
54+
[JsonSerializable](http://php.net/JsonSerializable) to ensure your object is correctly serialized.
55+
56+
Just like the `html()` factory, the `json()` factory allows passing two additional arguments — a
57+
status code, and an array of headers — to allow you to further seed the initial state of the
58+
response.
59+
60+
## Empty Responses
61+
62+
Many API actions allow returning empty responses:
63+
64+
- `201 Created` responses are often empty, and only include a `Link` or `Location` header pointing
65+
to the newly created resource.
66+
- `202 Accepted` responses are typically empty, indicating that the new entity has been received,
67+
but not yet processed.
68+
- `204 No Content` responses are, by definition, empty, and often used as a success response when
69+
deleting an entity.
70+
71+
`Zend\Diactoros\Response\EmptyResponse` is a `Zend\Diactoros\Response` extension that, by default,
72+
returns an empty response with a 204 status. Its constructor allows passing the status and headers
73+
only:
74+
75+
```php
76+
class EmptyResponse extends Response
77+
{
78+
public function __construct($status = 204, array $headers = []);
79+
}
80+
```
81+
82+
An empty, read-only body is injected at instantiation, ensuring no write operations are possible on
83+
the response. Usage is typically one of the following forms:
84+
85+
```php
86+
// Basic 204 response:
87+
$response = new EmptyResponse();
88+
89+
// 201 response with location header:
90+
$response = new EmptyResponse(201, [
91+
'Location' => [ $url ],
92+
]);
93+
94+
// Alternately, set the header after instantiation:
95+
$response = ( new EmptyResponse(201) )->withHeader('Location', $url);
96+
```
97+
98+
## Creating custom responses
99+
100+
PHP allows constructor overloading. What this means is that constructors of extending classes can
101+
define completely different argument sets without conflicting with the parent implementation.
102+
Considering that most custom response types do not need to change internal functionality, but
103+
instead focus on user experience (i.e., simplifying instantiation), this fact can be leveraged to
104+
create your custom types.
105+
106+
The general pattern will be something like this:
107+
108+
```php
109+
class MyCustomResponse extends Response
110+
{
111+
public function __construct($data, $status = 200, array $headers = [])
112+
{
113+
// - Do something with $data, and create a Stream for the body (if necessary).
114+
// - Maybe set some default headers.
115+
116+
parent::__construct($body, $status, $headers);
117+
}
118+
}
119+
```
120+
121+
Note the call to `parent::__construct()`. This is particularly relevant, as the implementation at
122+
the time of writing has all class properties marked as private, making them inaccessible to
123+
extensions; this is done to protect encapsulation and ensure consistency of operations between
124+
instances.
125+
126+
If you don't want to go the extension route (perhaps you don't want another `ResponseInterface`
127+
implementation within your object graph) you can instead create a factory.
128+
[StringResponse](https://github.com/zendframework/zend-diactoros/tree/master/src/Response/StringResponse.php)
129+
provides one such example. We recommend the following semantics:
130+
131+
```php
132+
function ($dataOrMessage, $status = 200, array $headers = []);
133+
```
134+
135+
These ensure consistency of factories, and allow consumers to provide the status and
136+
instance-specific headers on creation. (Obviously, specify different defaults as necessary.)

doc/bookdown.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"book/overview.md",
55
"book/install.md",
66
"book/usage.md",
7+
"book/custom-responses.md",
78
"book/emitting-responses.md",
89
"book/serialization.md",
910
"book/api.md"

0 commit comments

Comments
 (0)