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

Commit 0a51e19

Browse files
committed
Provides all documentation!
1 parent d97a513 commit 0a51e19

File tree

6 files changed

+278
-2
lines changed

6 files changed

+278
-2
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,19 @@
33
[![Build Status](https://secure.travis-ci.org/zendframework/zend-httphandlerrunner.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-httphandlerrunner)
44
[![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-httphandlerrunner/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-httphandlerrunner?branch=master)
55

6-
This library provides ...
6+
This library provides utilities for:
7+
8+
- Emitting [PSR-7](https://www.php-fig.org/psr/psr-7) responses.
9+
- Running [PSR-15](https://www.php-fig.org/psr/psr-15) server request handlers,
10+
which involves marshaling a PSR-7 `ServerRequestInterface`, handling
11+
exceptions due to request creation, and emitting the response returned by the
12+
composed request handler.
13+
14+
The `RequestHandlerRunner` will be used in the bootstrap of your application to
15+
fire off the `RequestHandlerInterface` representing your application.
16+
17+
If you are developing an async application, you will more than likely not want
18+
to use this package.
719

820
## Installation
921

docs/book/emitters.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Emitters
2+
3+
Emitters are used to _emit_ a [PSR-7](https://www.php-fig.org/psr/psr-7)
4+
response. This should generally happen when running under a traditional PHP
5+
server API that uses output buffers, such as Apache or php-fpm.
6+
7+
Emitters are described by `Zend\HttpHandlerRunner\Emitter\EmitterInterface`:
8+
9+
```php
10+
use Psr\Http\Message\ResponseInterface;
11+
12+
interface EmitterInterface
13+
{
14+
public function emit(ResponseInterface $response) : bool;
15+
}
16+
```
17+
18+
Typically, such emitters will perform the following:
19+
20+
- Emit a response status line.
21+
- Emit all response headers.
22+
- Emit the response body.
23+
24+
(The first two items may be swapped in order; many SAPIs allow emitting multiple
25+
status lines, and will use the last one present. As such, most implementations
26+
will emit the status line after the headers to ensure the correct one is emitted
27+
by the SAPI.)
28+
29+
The `emit()` method allows returning a boolean. This value can be checked to
30+
determine if the emitter was able to emit the response. This capability is used
31+
by the provided `EmitterStack` to allow composing multiple emitters that can
32+
introspect the response to determine whether or not they are capable of emitting
33+
it.
34+
35+
## SapiEmitter
36+
37+
`Zend\HttpHandlerRunner\Emitter\SapiEmitter` accepts the response instance, and
38+
uses the built-in PHP function `header()` to emit both the headers as well as
39+
the status line. It then uses `echo` to emit the response body.
40+
41+
Internally, it also does a number of verifications:
42+
43+
- If headers have been previously sent, it will raise an exception.
44+
- If output has been previously sent, it will raise an exception.
45+
46+
These are performed in order to ensure the integrity of the response emitted.
47+
48+
It also filters header names to normalize them; this is done in part to ensure
49+
that if multiple headers of the same name are emitted, the SAPI will report them
50+
correctly.
51+
52+
This emitter can _always_ handle a response, and thus _always_ returns true.
53+
54+
## SapiStreamEmitter
55+
56+
`Zend\HttpHandlerRunner\Emitter\SapiStreamEmitter` behaves similarly to the
57+
`SapiEmitter`, with two key differences:
58+
59+
- It allows emitting a a _content range_, if a `Content-Range` header is
60+
specified in the response.
61+
- It will iteratively emit a range of bytes from the response, based on the
62+
buffer length provided to the emitter during construction. This is
63+
particularly useful when returning _files_ or _downloads_.
64+
65+
The emitter accepts an integer argument to the constructor, indicating the
66+
maximum buffer length; by default, this is 8192 bytes.
67+
68+
This emitter can _always_ handle a response, and thus _always_ returns true.
69+
70+
## EmitterStack
71+
72+
`Zend\HttpHandlerRunner\Emitter\EmitterStack` allows providing a
73+
last-in-first-out (LIFO) stack of emitters instead of a single emitter. If an
74+
emitter is incapable of handling the response and returns `false`, the stack
75+
will move to the next emitter. If an emitter returns `true`, the stack
76+
short-circuits and immediately returns.
77+
78+
The `EmitterStack` extends `SplStack`, and thus allows you to add emitters using
79+
any of the methods that class defines; we recommend adding them in LIFO order
80+
using `push()`:
81+
82+
```php
83+
$stack->push($last);
84+
$stack->push($second);
85+
$stack->push($first);
86+
```
87+
88+
### Conditionally using the SapiStreamEmitter
89+
90+
The `SapiStreamEmitter` is capable of emitting any response. However, for
91+
in-memory responses, you may want to use the more efficient `SapiEmitter`. How
92+
can you do this?
93+
94+
One way is to check for response artifacts that indicate a file download, such
95+
as the `Content-Disposition` or `Content-Range` headers; if those headers are
96+
not present, you could return `false` from the emitter, and otherwise continue.
97+
You could achieve this by decorating the `SapiStreamEmitter`:
98+
99+
```php
100+
$sapiStreamEmitter = new SapiStreamEmitter($maxBufferLength);
101+
$conditionalEmitter = new class ($sapiStreamEmitter) implements EmitterInterface {
102+
private $emitter;
103+
104+
public function __construct(EmitterInterface $emitter)
105+
{
106+
$this->emitter = $emitter;
107+
}
108+
109+
public function emit(ResponseInterface) : bool
110+
{
111+
if (! $response->hasHeader('Content-Disposition')
112+
&& ! $response->hasHeader('Content-Range')
113+
) {
114+
return false;
115+
}
116+
return $this->emitter->emit($response);
117+
}
118+
};
119+
120+
$stack = new EmitterStack();
121+
$stack->push(new SapiEmitter());
122+
$stack->push($conditionalEmitter);
123+
```
124+
125+
In this way, you can have the best of both worlds, using the memory-efficient
126+
`SapiStreamEmitter` for large file downloads or streaming buffers, and the
127+
general-purpose `SapiEmitter` for everything else.

docs/book/intro.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
# zend-httphandlerrunner
22

3-
This component provides ...
3+
This component provides utilities for:
4+
5+
- Emitting [PSR-7](https://www.php-fig.org/psr/psr-7) responses.
6+
- Running [PSR-15](https://www.php-fig.org/psr/psr-15) server request handlers,
7+
which involves marshaling a PSR-7 `ServerRequestInterface`, handling
8+
exceptions due to request creation, and emitting the response returned by the
9+
composed request handler.
10+
11+
The `RequestHandlerRunner` will be used in the bootstrap of your application to
12+
fire off the `RequestHandlerInterface` representing your application.
13+
14+
If you are developing an async application, you will more than likely not want
15+
to use this package.

docs/book/runner.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# The Request Handler Runner
2+
3+
`Zend\HttpHandlerRunner\RequestHandlerRunner` can be used to _run_ a
4+
[PSR-15](https://www.php-fig.org/psr/psr-15) `RequestHandlerInterface` instance.
5+
By this, we mean:
6+
7+
- Marshal a server request instance.
8+
- Handle exceptions due to marshaling the server request instance.
9+
- Pass the request to the composed request handler.
10+
- Pass the response generated by the request handler to the composed [emitter](emitters.md)
11+
12+
The runner takes four constructor arguments, in the following order:
13+
14+
- A `Psr\Http\Server\RequestHandlerInterface` instance.
15+
- A `Zend\HttpHandlerRunner\Emitter\EmitterInterface` instance.
16+
- A PHP callable to generate a `Psr\Http\Message\ServerRequestInterface` instance.
17+
- A PHP callable that can generate a `Psr\Http\Message\ResponseInterface`
18+
instance given a PHP `Throwable` generated by the server request factory
19+
from the third argument.
20+
21+
Once constructed, you may call the method `run()`:
22+
23+
```php
24+
$runner->run();
25+
```
26+
27+
## Server request factory
28+
29+
The `$serverRequestFactory` argument to the constructor may be any PHP callable
30+
that accepts no arguments (or all optional arguments), and which returns a
31+
[PSR-7](https://www.php-fig.org/psr/psr-7) `ServerRequestInterface` instance.
32+
33+
As an example, using [zend-diactoros](https://docs.zendframework.com/zend-diactoros):
34+
35+
```php
36+
use Zend\Diactoros\ServerRequestFactory;
37+
38+
$serverRequestFactory = [ServerRequestFactory::class, 'fromGlobals'];
39+
```
40+
41+
Alternately, a [PSR-17 (proposed specification for HTTP message
42+
factories)](https://github.com/php-fig/fig-standards/tree/a6abdeb44a060d80925c95714f39854c510e879f/proposed/http-factory) factory may be used:
43+
44+
```php
45+
use HttpInterop\Implementation\ServerRequestFactory;
46+
47+
$serverRequestFactory = function () use ($_SERVER, $factory) {
48+
return $factory->createServerRequestFromArray($_SERVER);
49+
};
50+
```
51+
52+
## Server request error response generator
53+
54+
In rare cases, the server request factory may raise a PHP `Throwable` or
55+
`Exception`. In those cases, the runner still needs to generate a response to
56+
emit.
57+
58+
The server request error response generator argument to the
59+
`RequestHandlerRunner` constructor may be any PHP callable that accepts a PHP
60+
`Throwable` argument, and which returns a PSR-7 `ResponseInterface` instance.
61+
62+
As an example, [zend-stratigility](https://docs.zendframework.com/zend-stratigility/)
63+
provides the class `Zend\Stratigility\Middleware\ErrorResponseGenerator`, which
64+
is typically used with its `ErrorHandler` to generate a response. It can be
65+
re-purposed if we curry in empty server request and response instances as
66+
follows:
67+
68+
```php
69+
use Throwable;
70+
use Zend\Diactoros\Response;
71+
use Zend\Diactoros\ServerRequest;
72+
use Zend\Stratigility\Middleware\ErrorResponseGenerator;
73+
74+
$errorResponseGenerator = function (Throwable $e) {
75+
$generator = new ErrorResponseGenerator();
76+
return $generator($e, new ServerRequest(), new Response());
77+
};
78+
```
79+
80+
## Request handlers
81+
82+
Request handlers MUST implement `Psr\Http\Server\RequestHandlerInterface`.
83+
Additionally, for best results, they MUST provide their own error and exception
84+
handling, to ensure that a response is returned and the runner is able to emit a
85+
response.

docs/book/usage.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Installation and Usage
2+
3+
To install this package, run the following [composer](https://getcomposer.org)
4+
command:
5+
6+
```bash
7+
$ composer require zendframework/zend-httphandlerrunner
8+
```
9+
10+
The package provides both [emitters](emitters.md) and the [request handler
11+
runner](runner.md), and these are generally used within the bootstrap of your
12+
application.
13+
14+
We recommend using a dependency injection container to define your various
15+
instances, including the [PSR-15](https://www.php-fig.org/psr/psr-15) request
16+
handler representing your application, the response emitter, the server request
17+
factory, the server request error response generator, potentially, the runner
18+
itself.
19+
20+
The example below instantiates the runner manually by pulling its dependencies
21+
from a configured [PSR-11](https://www.php-fig.org/psr/psr-11) container.
22+
23+
```php
24+
use Zend\HttpHandlerRunner\Emitter\EmitterStack;
25+
use Zend\HttpHandlerRunner\RequestHandlerRunner;
26+
27+
$container = require 'config/container.php';
28+
29+
$runner = new RequestHandlerRunner(
30+
$container->get(ApplicationRequestHandler::class),
31+
$container->get(EmitterStack::class),
32+
$container->get('ServerRequestFactory'),
33+
$container->get('ServerRequestErrorResponseGenerator')
34+
);
35+
$runner->run();
36+
```

mkdocs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ site_dir: docs/html
33
pages:
44
- index.md
55
- Introduction: intro.md
6+
- Usage: usage.md
7+
- Reference:
8+
- "Request Handler Runner": runner.md
9+
- Emitters: emitters.md
610
site_name: zend-httphandlerrunner
711
site_description: 'Execute PSR-15 RequestHandlerInterface instances and emit responses they generate.'
812
repo_url: 'https://github.com/zendframework/zend-httphandlerrunner'

0 commit comments

Comments
 (0)