Skip to content

Commit

Permalink
Application: change public properties (onRequest,onResponse) to addLi…
Browse files Browse the repository at this point in the history
…stener
  • Loading branch information
f3l1x committed Nov 15, 2017
1 parent 38f3841 commit 663b261
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 34 deletions.
24 changes: 23 additions & 1 deletion .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ The middlewares / relay conception is a strong pattern with many benefits.
## Content

- [Installation - how to register an extension](#installation)
- [Modes - nette/standalone mode](#application)
- [Modes - nette/standalone mode](#modes)
- [Application - life cycle](#application)
- [Middlewares](#middlewares)
- [AbstractRootMiddleware](#abstractrootmiddleware)
- [AutoBasePathMiddleware](#autobasepathmiddleware)
Expand Down Expand Up @@ -49,6 +50,27 @@ $container->getByType(Contributte\Middlewares\Application\IApplication::class)->

That's all. The main purpose of this is to start via our application, not the default one `Nette\Application\Application`.

## Application

`AbstractApplication` adds a life cycle events you can interact with. There are 4 events:

- `startup` - triggered when `$app->run()` is called
- `startup` - triggered before the chain is called
- `error` - triggered when exceptions is ocurred
- `response` - triggered after the chain is called

You attach listener calling the method `$app->addListener(type, callback)`.

```yaml
services:
middleware.application:
setup:
- addListener(startup, [@logger, 'logStartup'])
- addListener(request, [@logger, 'logRequest'])
- addListener(error, [@logger, 'logError'])
- addListener(response, [@logger, 'logResponse'])
```

## Middlewares

Build your own middleware chain cannot be easier. Just place your middleware (services) under `middleware` section.
Expand Down
55 changes: 40 additions & 15 deletions src/Application/AbstractApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Contributte\Middlewares\Application;

use Contributte\Middlewares\Exception\InvalidStateException;
use Contributte\Middlewares\IMiddleware;
use Exception;
use Psr\Http\Message\ResponseInterface;
Expand All @@ -14,24 +15,25 @@
abstract class AbstractApplication implements IApplication
{

/** @var callable[] */
public $onStartup = [];

/** @var callable[] */
public $onRequest = [];

/** @var callable[] */
public $onError = [];

/** @var callable[] */
public $onResponse = [];
const LISTENER_STARTUP = 'startup';
const LISTENER_REQUEST = 'request';
const LISTENER_ERROR = 'error';
const LISTENER_RESPONSE = 'response';

/** @var callable|IMiddleware */
private $chain;

/** @var bool */
private $catchExceptions = FALSE;

/** @var [callable[]] */
private $listeners = [
self::LISTENER_STARTUP => [],
self::LISTENER_REQUEST => [],
self::LISTENER_ERROR => [],
self::LISTENER_RESPONSE => [],
];

/**
* @param callable|IMiddleware $chain
*/
Expand All @@ -57,15 +59,15 @@ public function setCatchExceptions($catch = TRUE)
public function run()
{
// Trigger event!
$this->dispatch($this->onStartup, [$this]);
$this->dispatch($this->listeners[self::LISTENER_STARTUP], [$this]);

// Create initial request & response (PSR7!)
$request = $this->createInitialRequest();
$response = $this->createInitialResponse();

try {
// Trigger event!
$this->dispatch($this->onRequest, [$this, $request, $response]);
$this->dispatch($this->listeners[self::LISTENER_REQUEST], [$this, $request, $response]);

// Right to the cycle
$response = call_user_func(
Expand All @@ -83,15 +85,15 @@ function (ServerRequestInterface $request, ResponseInterface $response) {
}
} catch (Exception $e) {
// Trigger event! In case of manual handling error, returned object is passed.
$res = $this->dispatch($this->onError, [$this, $e, $request, $response]);
$res = $this->dispatch($this->listeners[self::LISTENER_ERROR], [$this, $e, $request, $response]);
if ($res !== NULL && $res !== FALSE) return $res;

// Throw exception again if it's not caught
if ($this->catchExceptions !== TRUE) throw $e;
}

// Trigger event! In case of manual finalizing, returned object is passed.
$res = $this->dispatch($this->onResponse, [$this, $request, $response]);
$res = $this->dispatch($this->listeners[self::LISTENER_RESPONSE], [$this, $request, $response]);
if ($res !== NULL && $res !== FALSE) return $res;

// Send to finalizer (simple send response)
Expand All @@ -115,6 +117,29 @@ abstract protected function createInitialResponse();
*/
abstract protected function finalize(ServerRequestInterface $request, ResponseInterface $response);

/**
* LISTENERS ***************************************************************
*/

/**
* @param string $type
* @param callable $listener
* @return void
*/
public function addListener($type, $listener)
{
if (!in_array($type, [
self::LISTENER_STARTUP,
self::LISTENER_REQUEST,
self::LISTENER_ERROR,
self::LISTENER_RESPONSE,
])) {
throw new InvalidStateException(sprintf('Given type "%s" is not supported'));
}

$this->listeners[$type][] = $listener;
}

/**
* HELPERS *****************************************************************
*/
Expand Down
36 changes: 18 additions & 18 deletions tests/cases/Application/MiddlewareApplication.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ test(function () {
};

$app = new MiddlewareApplication($callback);
$app->onError[] = function (MiddlewareApplication $app, Exception $e, ServerRequestInterface $req, ResponseInterface $res) {
$app->addListener($app::LISTENER_ERROR, function (MiddlewareApplication $app, Exception $e, ServerRequestInterface $req, ResponseInterface $res) {
Notes::add('CALLED');
};
});

Assert::throws(function () use ($app) {
$app->run();
Expand All @@ -104,9 +104,9 @@ test(function () {

$app = new MiddlewareApplication($callback);
$app->setCatchExceptions(TRUE);
$app->onError[] = function (MiddlewareApplication $app, Exception $e, ServerRequestInterface $req, ResponseInterface $res) {
$app->addListener($app::LISTENER_ERROR, function (MiddlewareApplication $app, Exception $e, ServerRequestInterface $req, ResponseInterface $res) {
Notes::add('CALLED');
};
});

$app->run();
Assert::equal(['CALLED'], Notes::fetch());
Expand All @@ -119,11 +119,11 @@ test(function () {
};

$app = new MiddlewareApplication($callback);
$app->onError[] = function (MiddlewareApplication $app, Exception $e, ServerRequestInterface $req, ResponseInterface $res) {
$app->addListener($app::LISTENER_ERROR, function (MiddlewareApplication $app, Exception $e, ServerRequestInterface $req, ResponseInterface $res) {
Notes::add('CALLED');

return 'OK';
};
});

Assert::equal('OK', $app->run());
Assert::equal(['CALLED'], Notes::fetch());
Expand All @@ -138,15 +138,15 @@ test(function () {
};

$app = new MiddlewareApplication($callback);
$app->onStartup[] = function (MiddlewareApplication $app) {
$app->addListener($app::LISTENER_STARTUP, function (MiddlewareApplication $app) {
Notes::add('STARTUP');
};
$app->onRequest[] = function (MiddlewareApplication $app, ServerRequestInterface $req, ResponseInterface $res) {
});
$app->addListener($app::LISTENER_REQUEST, function (MiddlewareApplication $app, ServerRequestInterface $req, ResponseInterface $res) {
Notes::add('REQUEST');
};
$app->onResponse[] = function (MiddlewareApplication $app, ServerRequestInterface $req, ResponseInterface $res) {
});
$app->addListener($app::LISTENER_RESPONSE, function (MiddlewareApplication $app, ServerRequestInterface $req, ResponseInterface $res) {
Notes::add('RESPONSE');
};
});

Assert::equal('OK', (string) $app->run()->getBody());
Assert::equal(['STARTUP', 'REQUEST', 'RESPONSE'], Notes::fetch());
Expand All @@ -162,21 +162,21 @@ test(function () {
};

$app = new MiddlewareApplication($callback);
$app->onStartup[] = function (MiddlewareApplication $app) {
$app->addListener($app::LISTENER_STARTUP, function (MiddlewareApplication $app) {
Notes::add('STARTUP1');

return '1';
};
$app->onStartup[] = function (MiddlewareApplication $app, $prev) {
});
$app->addListener($app::LISTENER_STARTUP, function (MiddlewareApplication $app, $prev) {
Notes::add('STARTUP2');
Notes::add($prev);

return '2';
};
$app->onStartup[] = function (MiddlewareApplication $app, $prev) {
});
$app->addListener($app::LISTENER_STARTUP, function (MiddlewareApplication $app, $prev) {
Notes::add('STARTUP3');
Notes::add($prev);
};
});

Assert::equal('OK', (string) $app->run()->getBody());
Assert::equal(['STARTUP1', 'STARTUP2', '1', 'STARTUP3', '2'], Notes::fetch());
Expand Down

0 comments on commit 663b261

Please sign in to comment.