silexphp/Silex — pull request #902 by gigablah (2014-02-04 04:27:51+00:00)
Title: Proposal: Generator-style middleware

Seeing as PHP 5.5 adds support for [generators](http://www.php.net/manual/en/language.generators.overview.php), I experimented with generator-style middleware in Silex. The basic idea is something like:

```php
$app->use(function () use ($app) {
$start = microtime(true);
$response = yield;
$response->headers->set('X-Response-Time', sprintf('%fms', microtime(true) - $start));
});
```

With this particular implementation I tap into the `EventDispatcher` so it makes use of events (`use` is a reserved keyword so I went with `utilize`):

```php
$app->utilize(function ($event) use ($app) {
$start = microtime(true);
$event = yield;
if ($event->isMasterRequest()) {
$response = $event->getResponse();
$response->headers->set('X-Response-Time', sprintf('%fms', microtime(true) - $start));
}
});
```

`utilize()` will register the listener function with the `REQUEST` and `RESPONSE` events by default, but you can specify any event/priority combination as the second parameter:

```php
$app->utilize(function ($event) use ($app) {
// GetResponseEvent
$request = $event->getRequest();

// GetResponseForControllerResultEvent
$event = yield;
$result = $event->getControllerResult();

// FilterResponseEvent
$event = yield;
$response = $event->getResponse();
}, array(
KernelEvents::REQUEST => 16,
KernelEvents::VIEW => -16,
KernelEvents::RESPONSE => 0
));
```

I implemented it as a trait, since (obviously) it only works with PHP 5.5 and HHVM.

Example usage:

```php
require_once './vendor/autoload.php';

class MyApplication extends Silex\Application
{
use Silex\Application\MiddlewareTrait;
}

$app = new MyApplication();

// You can define middleware as a regular closure
$app->utilize(function () {
//...
});

// Or you can define it as a separate class
class ResponseTimeMiddleware
{
public function __invoke()
{
$start = microtime(true);

$event = yield;
$response = $event->getResponse();

$end = microtime(true);

$response->headers->set('X-Response-Time', sprintf('%fms', $end - $start));
}

// for demonstration purposes
public function run()
{
$this->__invoke();
}
}

$app->utilize(new ResponseTimeMiddleware());

// You can also make use of the callback resolver to lazy load classes
$app['response_time_middleware'] = $app->share(function ($app) {
return new ResponseTimeMiddleware();
});

$app->utilize('response_time_middleware:run');

// Register multiple middlewares
$app->utilize([
new ResponseTimeMiddleware(),
new ContentNegotiationMiddleware(),
new LayoutMiddleware(),
...
]);
```

What do you think?