Skip to content

Commit 0e24065

Browse files
authored
Merge pull request #69 from http-interop/fix/replace-readme
Replace readme with a link to PSR-15
2 parents 9a801fe + ec3ad17 commit 0e24065

File tree

1 file changed

+3
-316
lines changed

1 file changed

+3
-316
lines changed

README.md

Lines changed: 3 additions & 316 deletions
Original file line numberDiff line numberDiff line change
@@ -1,320 +1,7 @@
11
HTTP Server Middleware
22
======================
33

4-
1. Summary
5-
----------
4+
This is an implementation of the proposed [PSR-15][psr-15]. Please refer to the
5+
proposal for a description.
66

7-
The purpose of this PSR is to provide an interface that defines the formal
8-
method signature for HTTP Server Middleware that is compatible with HTTP
9-
Messages, as defined in [PSR-7][psr7].
10-
11-
[psr7]: http://www.php-fig.org/psr/psr-7/
12-
13-
2. Why Bother?
14-
--------------
15-
16-
The HTTP Messages specification does not contain any reference to HTTP Middleware.
17-
18-
The design pattern used by middleware has existed for many years as the
19-
[pipeline pattern][pipeline], or more specifically, "linear pipeline processing".
20-
The general concept of reusable middleware was popularized within PHP by
21-
[StackPHP][stackphp]. Since the release of the HTTP Messages standard, a number
22-
of frameworks have adopted middleware that use HTTP Message interfaces.
23-
24-
Agreeing on a formal server middleware interface eliminates several problems and
25-
provides a number of benefits:
26-
27-
* Provides a formal standard for middleware developers to commit to.
28-
* Eliminates duplication of similar interfaces defined by various frameworks.
29-
* Avoids minor discrepancies in method signatures.
30-
* Enables any middleware component to run in any compatible framework.
31-
32-
[pipeline]: https://en.wikipedia.org/wiki/Pipeline_(computing)
33-
[stackphp]: http://stackphp.com/
34-
35-
3. Scope
36-
--------
37-
38-
### 3.1 Goals
39-
40-
* Create a middleware interface that uses HTTP Messages.
41-
* Ensure that middleware will not be coupled to a specific implementation of HTTP Messages.
42-
* Implement a middleware signature that is based on best practices.
43-
44-
### 3.2 Non-Goals
45-
46-
* Attempting to define how middleware is dispatched.
47-
* Attempting to define interfaces for client/asynchronous middleware.
48-
* Attempting to define the mechanism by which HTTP responses are created.
49-
50-
4. Approaches
51-
-------------
52-
53-
There are currently two common approaches to server middleware that use HTTP Messages.
54-
55-
### 4.1 Double Pass
56-
57-
The signature used by most middleware implementations has been mostly the same
58-
and is based on [Express middleware][express], which is defined as:
59-
60-
```
61-
fn(request, response, next): response
62-
```
63-
64-
[express]: http://expressjs.com/en/guide/writing-middleware.html
65-
66-
Based on the middleware implementations already used by frameworks that have
67-
adopted this signature, the following commonalities are observed:
68-
69-
* The middleware is defined as a [callable][php-callable].
70-
* The middleware is passed 3 arguments during invocation:
71-
1. A `ServerRequestInterface` implementation.
72-
2. A `ResponseInterface` implementation.
73-
3. A `callable` that receives the request and response to delegate the next middleware.
74-
75-
[php-callable]: http://php.net/manual/language.types.callable.php
76-
77-
A significant number of projects provide and/or use exactly the same interface.
78-
This approach is often referred to as "double pass" in reference to both the
79-
request and response being passed to the middleware.
80-
81-
#### 4.1.1 Projects Using Double Pass
82-
83-
* [mindplay/middleman](https://github.com/mindplay-dk/middleman/blob/1.0.0/src/MiddlewareInterface.php#L24)
84-
* [relay/relay](https://github.com/relayphp/Relay.Relay/blob/1.0.0/src/MiddlewareInterface.php#L24)
85-
* [slim/slim](https://github.com/slimphp/Slim/blob/3.4.0/Slim/MiddlewareAwareTrait.php#L66-L75)
86-
* [zendframework/zend-stratigility](https://github.com/zendframework/zend-stratigility/blob/1.0.0/src/MiddlewarePipe.php#L69-L79)
87-
88-
#### 4.1.2 Middleware Implementing Double Pass
89-
90-
* [bitexpert/adroit](https://github.com/bitExpert/adroit)
91-
* [akrabat/rka-ip-address-middleware](https://github.com/akrabat/rka-ip-address-middleware)
92-
* [akrabat/rka-scheme-and-host-detection-middleware](https://github.com/akrabat/rka-scheme-and-host-detection-middleware)
93-
* [bear/middleware](https://github.com/bearsunday/BEAR.Middleware)
94-
* [hannesvdvreken/psr7-middlewares](https://github.com/hannesvdvreken/psr7-middlewares)
95-
* [los/api-problem](https://github.com/Lansoweb/api-problem)
96-
* [los/los-rate-limit](https://github.com/Lansoweb/LosRateLimit)
97-
* [monii/monii-action-handler-psr7-middleware](https://github.com/monii/monii-action-handler-psr7-middleware)
98-
* [monii/monii-nikic-fast-route-psr7-middleware](https://github.com/monii/monii-nikic-fast-route-psr7-middleware)
99-
* [monii/monii-response-assertion-psr7-middleware](https://github.com/monii/monii-response-assertion-psr7-middleware)
100-
* [mtymek/blast-base-url](https://github.com/mtymek/blast-base-url)
101-
* [ocramius/psr7-session](https://github.com/Ocramius/PSR7Session)
102-
* [oscarotero/psr7-middlewares](https://github.com/oscarotero/psr7-middlewares)
103-
* [php-middleware/block-robots](https://github.com/php-middleware/block-robots)
104-
* [php-middleware/http-authentication](https://github.com/php-middleware/http-authentication)
105-
* [php-middleware/log-http-messages](https://github.com/php-middleware/log-http-messages)
106-
* [php-middleware/maintenance](https://github.com/php-middleware/maintenance)
107-
* [php-middleware/phpdebugbar](https://github.com/php-middleware/phpdebugbar)
108-
* [php-middleware/request-id](https://github.com/php-middleware/request-id)
109-
* [relay/middleware](https://github.com/relayphp/Relay.Middleware)
110-
111-
The primary downside of this interface is that the while the interface itself is
112-
a callable, there is currently no way to type hint a closure in a similar way.
113-
114-
### 4.2 Single Pass (Lambda)
115-
116-
The other approach to middleware is much closer to [StackPHP][stackphp] style
117-
and is defined as:
118-
119-
```
120-
fn(request, frame): response
121-
```
122-
123-
Middleware taking this approach generally has the following commonalities:
124-
125-
* The middleware is defined with a specific interface with a method that takes
126-
the request for processing.
127-
* The middleware is passed 2 arguments during invocation:
128-
1. An object that represents an HTTP request.
129-
2. A delegate that receives the request to dispatch next middleware in the pipeline.
130-
131-
In this form, middleware has no access to a response until one is generated by
132-
innermost middleware. Middleware can then modify the response before returning
133-
back up the stack.
134-
135-
This approach is often referred to as "single pass" or "lambda" in reference to
136-
only the request being passed to the middleware.
137-
138-
[php-closure]: http://php.net/closure
139-
140-
#### 4.2.1 Projects Using Single Pass
141-
142-
There are fewer examples of this approach within projects using HTTP Messages,
143-
with one notable exception.
144-
145-
[Guzzle middleware][guzzle-middleware] is focused on outgoing (client) requests
146-
and uses this signature:
147-
148-
```
149-
function(RequestInterface $request, array $options): ResponseInterface
150-
```
151-
152-
#### 4.2.2 Additional Projects Using Single Pass
153-
154-
There are also significant projects that predate HTTP Messages using this approach.
155-
156-
[StackPHP][stackphp] is based on [Symfony HttpKernel][httpkernel] and supports
157-
middleware with this signature:
158-
159-
```
160-
handle(Request $request, $type, $catch): Response
161-
```
162-
163-
*__Note__: While Stack has multiple arguments, a response object is not included.*
164-
165-
[Laravel middleware][laravel-middleware] uses Symfony components and supports
166-
middleware with this signature:
167-
168-
```
169-
function handle(Request $request, callable $next): Response
170-
```
171-
172-
[guzzle-middleware]: http://docs.guzzlephp.org/en/latest/handlers-and-middleware.html
173-
[httpkernel]: https://symfony.com/doc/2.0/components/http_kernel/introduction.html
174-
[laravel-middleware]: https://laravel.com/docs/master/middleware
175-
176-
### 4.3 Comparison of Approaches
177-
178-
The single pass approach to middleware has been well established in the PHP
179-
community for many years. This is most evident with the large number of packages
180-
that are based around StackPHP.
181-
182-
The double pass approach is much newer but has been almost universally used by
183-
early adopters of HTTP Messages.
184-
185-
### 4.4 Chosen Approach
186-
187-
Despite the nearly universal adoption of the double-pass approach there are
188-
significant issues regarding implementation.
189-
190-
The most severe is that passing an empty response has no guarantees that the
191-
response is in a usable state. This is further exacerbated by the fact that a
192-
middleware may modify the response before passing it for further dispatching.
193-
194-
Further compounding the problem is that there is no way to ensure that the
195-
response body has not been written to, which can lead to incomplete output or
196-
error responses being sent with cache headers attached. It is also possible
197-
to end up with [corrupted body content][rob-allen-filtering] when writing over
198-
existing body content if the new content is shorter than the original. The most
199-
effective way to resolve these issues is to always provide a fresh stream when
200-
modifying the body of a message.
201-
202-
[rob-allen-filtering]: https://akrabat.com/filtering-the-psr-7-body-in-middleware/
203-
204-
Some have argued that passing the response helps ensure dependency inversion.
205-
While it is true that it helps avoid depending on a specific implementation of
206-
HTTP messages, the problem can also be resolved by injecting factories into the
207-
middleware to create HTTP message objects, or by injecting empty message instances.
208-
With the creation of HTTP Factories in [PSR-17][psr17], a standard approach to
209-
handling dependency inversion is possible.
210-
211-
[psr17]: https://github.com/php-fig/fig-standards/blob/master/proposed/http-factory/http-factory-meta.md
212-
213-
A more subjective, but also important, concern is that existing double-pass
214-
middleware typically uses the `callable` type hint to refer to middleware.
215-
This makes strict typing impossible, as there is no assurance that the `callable`
216-
being passed implements a middleware signature, which reduces runtime safety.
217-
218-
**Due to these significant issues the lambda approach has been choosen for this proposal.**
219-
220-
5. Design Decisions
221-
-------------------
222-
223-
### 5.1 Middleware Design
224-
225-
The `MiddlewareInterface` defines a single method that accepts a server
226-
request and a delegate and must return a response. The middleware may:
227-
228-
- Evolve the request before passing it to the delegate to execute the next
229-
available middleware.
230-
- Evolve the response received from the delegate before returning it.
231-
- Create and return a response without passing it to the delegate, thereby
232-
preventing any further middleware from executing.
233-
234-
#### Why doesn't middleware use `__invoke`?
235-
236-
Doing so would conflict with existing middleware that implements the double-pass
237-
approach and may want to implement the middleware interface.
238-
239-
In addition, classes that define `__invoke` can be type hinted as `callable`,
240-
which results in less strict typing. This is generally undesirable, especially
241-
when the `__invoke` method uses strict typing.
242-
243-
#### Why is a server request required?
244-
245-
To make it clear that the middleware can only be used in a synchronous, server
246-
side context.
247-
248-
While not all middleware will need to use the additional methods defined by the
249-
server request interface, external requests are typically handled asynchronously
250-
and would need to return a [promise][promises] of a response. (This is primarily
251-
due to the fact that multiple requests can be made in parallel and processed as
252-
they are returned.) It is outside the scope of this proposal to address the needs
253-
of asynchronous request/response life cycle.
254-
255-
Attempting to define client middleware would be premature at this point. Any future
256-
proposal that is focused on client side request processing should have the opportunity
257-
to define a standard that is specific to the nature of asynchronous middleware.
258-
259-
_See "client vs server side middleware" in [relevant links](#8-relevant-links) for
260-
additional information._
261-
262-
[promises]: https://promisesaplus.com/
263-
264-
### 5.2 Delegate Design
265-
266-
The `DelegateInterface` defines a single method that accepts a request and
267-
returns a response. The delegate interface must be implemented by any middleware
268-
dispatcher that uses middleware implementing `MiddlewareInterface`.
269-
270-
#### Why isn't the delegate a `callable`?
271-
272-
Using an interface type hint improves runtime safety and IDE support.
273-
274-
_See "discussion of FrameInterface" in [relevant links](#8-relevant-links) for
275-
additional information._
276-
277-
#### Why does the delegate conflict with middleware?
278-
279-
Both the middleware and delegate interface define a `process` method to prevent
280-
misuse of middleware as delegates.
281-
282-
The implementation of delegate should be defined within middleware dispatching systems.
283-
284-
6. People
285-
---------
286-
287-
### 6.1 Editor(s)
288-
289-
* Woody Gilk, <[email protected]>
290-
291-
### 6.2 Sponsors
292-
293-
* Paul M Jones, <[email protected]> (Coordinator)
294-
* Jason Coward, <[email protected]> (Sponsor)
295-
296-
### 6.3 Contributors
297-
298-
* Rasmus Schultz, <[email protected]>
299-
* Matthew Weier O'Phinney, <[email protected]>
300-
301-
7. Votes
302-
--------
303-
304-
* [Entrance Vote](https://groups.google.com/d/msg/php-fig/v9AijALWJhI/04XCwqgIEAAJ)
305-
* **Acceptance Vote:** _(not yet taken)_
306-
307-
8. Relevant Links
308-
-----------------
309-
310-
_**Note:** Order descending chronologically._
311-
312-
* [PHP-FIG mailing list thread](https://groups.google.com/d/msg/php-fig/vTtGxdIuBX8/NXKieN9vDQAJ)
313-
* [The PHP League middleware proposal](https://groups.google.com/d/msg/thephpleague/jyztj-Nz_rw/I4lHVFigAAAJ)
314-
* [PHP-FIG discussion of FrameInterface](https://groups.google.com/d/msg/php-fig/V12AAcT_SxE/aRXmNnIVCwAJ)
315-
* [PHP-FIG discussion about client vs server side middleware](https://groups.google.com/d/msg/php-fig/vBk0BRgDe2s/GTaT0yKNBgAJ)
316-
317-
9. Errata
318-
---------
319-
320-
...
7+
[psr-15]: https://github.com/php-fig/fig-standards/tree/master/proposed/http-middleware

0 commit comments

Comments
 (0)