Skip to content

Commit

Permalink
Merge pull request #8 from mindplay-dk/2.0.0
Browse files Browse the repository at this point in the history
2.0.0
  • Loading branch information
mindplay-dk authored Sep 29, 2016
2 parents 9b8f585 + 4ccea4e commit 03becb3
Show file tree
Hide file tree
Showing 10 changed files with 423 additions and 718 deletions.
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.idea
vendor
test/build
composer.lock
/.idea
/vendor
/test/build
/composer.lock
68 changes: 38 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mindplay/middleman
==================

Dead simple PSR-7 [middleware](#middleware) dispatcher.
Dead simple PSR-15 / PSR-7 [middleware](#middleware) dispatcher.

Provides (optional) integration with a [variety](https://github.com/container-interop/container-interop#compatible-projects)
of dependency injection containers compliant with [container-interop](https://github.com/container-interop/container-interop).

To upgrade from 1.x to 2.x, please see [UPGRADING.md](UPGRADING.md).

[![PHP Version](https://img.shields.io/badge/php-5.4%2B-blue.svg)](https://packagist.org/packages/mindplay/middleman)
[![Build Status](https://travis-ci.org/mindplay-dk/middleman.svg)](https://travis-ci.org/mindplay-dk/middleman)
[![Code Coverage](https://scrutinizer-ci.com/g/mindplay-dk/middleman/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/mindplay-dk/middleman/?branch=master)
Expand All @@ -15,19 +17,19 @@ Let's stop trying to make this complicated:

```php
use Psr\Http\Message\RequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Zend\Diactoros\Response;

$dispatcher = new Dispatcher([
function (Request $request, Response $response, callable $next) {
return $next($request, $response); // delegate control to next middleware
function (Request $request, callable $next) {
return $next($request); // delegate control to next middleware
},
function (Request $request, Response $response) {
return $response->withBody(...); // abort middleware stack and return the response
function (Request $request) {
return (new Response())->withBody(...); // abort middleware stack and return the response
},
// ...
]);

$result = $dispatcher->dispatch($request, $response);
$response = $dispatcher->dispatch($request);
```

For simplicity, the middleware stack itself is immutable - if you need a stack you can manipulate, `array`, `ArrayObject`, `SplStack` etc. are all fine choices.
Expand All @@ -36,11 +38,10 @@ If you prefer implementing middleware as a reusable class, just implement `__inv

```php
use Psr\Http\Message\RequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;

class MyMiddleware implements MiddlewareInteface
{
public function __invoke(Request $request, Response $response, callable $next) {
public function __invoke(Request $request, callable $next) {
// ...
}
}
Expand All @@ -57,7 +58,7 @@ $dispatcher = new Dispatcher(
RouterMiddleware::class,
ErrorMiddleware::class,
],
new InteropResolver($container)
new ContainerResolver($container)
);
```

Expand All @@ -80,51 +81,58 @@ If you're new to the concept of middleware, the following section will provide a
In a nutshell, a middleware component is a function (or [MiddlewareInterface](src/MiddlewareInterface.php) instance)
that takes an incoming (PSR-7) `RequestInterface` object, and returns a `ResponseInterface` object.

It does this in one of three ways: by *assuming*, *delegating*, or *sharing* control.
It does this in one of three ways: by *assuming*, *delegating*, or *sharing* responsibility
for the creation of a response object.

##### 1. Assuming Control
##### 1. Assuming Responsibility

When middleware *assumes* control, it doesn't delegate to the next middleware on the stack:
A middleware component *assumes* responsibility by creating and returning a response object,
rather than delegating to the next middleware on the stack:

```php
function ($request, $response, $next) {
return $response->withBody(...); // next middleware won't be run
use Zend\Diactoros\Response;

function ($request, $next) {
return (new Response())->withBody(...); // next middleware won't be run
}
```

Middleware near the top of the stack has the power to take away control from middleware
Middleware near the top of the stack has the power to completely bypass middleware
further down the stack.

##### 2. Delegating Control
##### 2. Delegating Responsibility

If middleware decides the request context isn't relevant to it, it may *delegate* control
to the next middleware on the stack:
By calling `$next`, middleware near the top of the stack may choose to fully delegate the
responsibility for the creation of a response to other middleware components
further down the stack:

```php
function ($request, $response, $next) {
function ($request, $next) {
if ($request->getMethod() !== 'POST') {
return $next($request, $response); // run the next middleware
return $next($request); // run the next middleware
} else {
// ...
}
}
```

Middleware near the top of the stack may choose to relinquish control, and delegate
the responsibility of producing a response, to middleware further down the stack.
Note that exhausting the middleware stack will result in an exception - it's assumed that
the last middleware component on the stack always produces a response of some sort, typically
a "404 not found" error page.

##### 3. Sharing Control
##### 3. Sharing Responsibility

When middleware *shares* control, it first delegates, and then resumes control:
Middleware near the top of the stack may choose to delegate responsibility for the creation of
the response to middleware further down the stack, and then make additional changes to
the returned response before returning it:

```php
function ($request, $response, $next) {
$result = $next($request, $response); // run the next middleware
function ($request, $next) {
$result = $next($request); // run the next middleware

return $result->withHeader(...); // then modify it's response
}
```

Middleware near the top of the stack may choose to first delegate control to middleware
further down the stack, then resume control, and possibly make additional changes to
the returned response.
The middleware component at the top of the stack ultimately has the most control, as it may
override any properties of the response object before returning.
101 changes: 101 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
Upgrading
=========

### From 1.x to 2.x

#### Name Change

`InteropResolver` was renamed to `ContainerResolver` - the method-signature has not changed, so you only need
to update your imports to reference the new name.

#### MiddlewareInterface Removed

The built-in `MiddlewareInterface` has been removed, and you need to select one of the two PSR-15 interfaces,
as described below.

If you used `callable` middleware, you should expect errors/exceptions, as middleman retains support for
`callable`, but now requires a PSR-15 compatible method-signature, as described below.

#### PSR-15 Middleware

`mindplay/middleman^2` adopts the [PSR-15](https://github.com/http-interop/http-middleware) `0.2` interfaces.

This changes the middleware signature substantially, from the legacy signature:

function (RequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface

To the PSR-15 signature:

function (RequestInterface|ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface

PSR-15 introduces two distinct interfaces: `MiddlewareInterface` for processing `RequestInterface`, and
`ServerMiddlewareInterface` for processing `ServerRequestInterface`.

Because the Response object no longer passes down through the middleware stack, you will need to port your
middleware components from the old signature to one of the new signatures.

For example, if you had something like the following:

```php
use mindplay\middleman\MiddlewareInterface;

class MyMiddleware implements MiddlewareInterface
{
public function __invoke(RequestInterface $request, ResponseInterface $response, $next)
{
if ($request instanceof ServerRequestInterface) {
if ($request->getUri()->getPath() === "/test") {
return $response->withBody(...);
}
}

return $next($request, $response->withHeader("X-Foo", "bar"));
}
}
```

You would need to change several things:

```php
use Interop\Http\Middleware\ServerMiddlewareInterface;

class MyMiddleware implements ServerMiddlewareInterface
{
public function process(ServerRequestInterface $request, DelegateInterface $next)
{
if ($request->getUri()->getPath() === "/test") {
return new Response()->withBody(...);
}

$response = $next->process($request);

if (! $response->hasHeader("X-Foo")) {
$response = $response->withHeader("X-Foo", "bar");
}

return $response;
}
}
```

That is:

1. The implemented interface (if any) changed from one provided by `middleman` to one provided by PSR-15 -
which means the method-name changed from `__invoke()` to `process()`.

2. In the case of server-middleware, the interface specifically type-hints the request as `ServerRequestInterface`,
removing the need to type-check within the implementation.

3. The delegate to the next middleware on the stack is type-hinted as `DelegateInterface`, and must now be
invoked by calling it's `process()` method.

4. The response argument no longer exists - this has two significant consequences:

1. If we're not going to delegate to the next middleware, the middleware component needs to construct the
response object by itself. (at the moment, this means that middleware that constructs a response needs to
depend on a PSR-7 implementation - this is the situation until
[PSR-17](https://github.com/php-fig/fig-standards/tree/master/proposed/http-factory) becomes available.)

2. In this example, we were decorating the response with a default header `X-Foo: bar`, which might have been
overwritten by the next middleware on the stack - instead, we now need to delegate to the next middleware
*first*, and then *conditionally* decorate with a default header-value, if one is not already present.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"require": {
"php": ">=5.4",
"psr/http-message": "~1.0",
"http-interop/http-middleware": "^0.2",
"container-interop/container-interop": "^1.1",
"mindplay/readable": "^1"
},
Expand Down
Loading

0 comments on commit 03becb3

Please sign in to comment.