Skip to content

Commit

Permalink
Update documentation, added shortcuts method to create Middleware fro…
Browse files Browse the repository at this point in the history
…m callable.
  • Loading branch information
noglitchyo committed Jun 25, 2019
1 parent 78c1832 commit 0d87530
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 51 deletions.
104 changes: 88 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,110 @@ Lightweight & simple PSR-15 server request handler implementation to handle midd
Request handler implementing the [RequestHandlerInterface](https://github.com/php-fig/http-server-handler/blob/master/src/RequestHandlerInterface.php)
and able to manage a collection of middlewares implementing the [MiddlewareInterface](https://github.com/php-fig/http-server-middleware/blob/master/src/MiddlewareInterface.php).

This middleware attempts to provide interoperability to process a collection of middlewares and
give the possibility to define the strategy on how middlewares are fetched.
This request handler attempts to provide interoperability to process a collection of middlewares and
give the possibility to define the strategy on how middlewares will be processed.

### Getting started

#### Requirements

- PHP 7.3

#### Installation

`composer require noglitchyo/middleware-collection-request-handler`

#### Run

The RequestHandler class only need 2 arguments:

- A default request handler (implementing [RequestHandlerInterface](https://github.com/php-fig/http-server-handler/blob/master/src/RequestHandlerInterface.php))
Instantiate the RequestHandler class. It requires ony 2 arguments:

It is there to provide the response if no middleware created one.
For example, you are using ADR pattern, the default request handler might be your Action.
It is also possible to directly provide a `callable` using `RequestHandler::fromCallable()`.
It will generate a generic instance of RequestHandlerInterface wrapping the `callable` inside.
- `$defaultRequestHandler` ([RequestHandlerInterface](https://github.com/php-fig/http-server-handler/blob/master/src/RequestHandlerInterface.php))

***Remember: the default request handler is responsible for providing a default response.***
***The default request handler is responsible to provide a default response if none of the middlewares created one.***

- A middleware collection (implementing [MiddlewareCollectionInterface](https://github.com/noglitchyo/middleware-collection-request-handler/blob/master/src/MiddlewareCollectionInterface.php))
Some examples of "default request handler":
- with the [ADR pattern](https://en.wikipedia.org/wiki/Action%E2%80%93domain%E2%80%93responder), the default request handler might be your action class.
- with the [MVC pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), the default request handler might be the action method of your controller.

It defines how you store your middlewares and the strategy to retrieve them.
Some implementations are provided by default and will probably fit your needs.
They offers different strategies varying from the Stack to the Queue.
It is possible to directly provide a `callable` using the factory method `RequestHandler::fromCallable()`.
It will generate a generic instance of RequestHandlerInterface wrapping the `callable` inside.

However, it is really simple to create you own collection if needed, since MiddlewareCollectionInterface requires only 3 methods.
- `$middlewareCollection` ([MiddlewareCollectionInterface](https://github.com/noglitchyo/middleware-collection-request-handler/blob/master/src/MiddlewareCollectionInterface.php))

Contains the middlewares and encapsulate the strategy used to store and retrieve the middlewares.
Some standard implementations are provided with different strategies: [stack (LIFO)](https://github.com/noglitchyo/middleware-collection-request-handler/blob/master/src/Collection/SplStackMiddlewareCollection.php), [queue (FIFO)](https://github.com/noglitchyo/middleware-collection-request-handler/blob/master/src/Collection/SplQueueMiddlewareCollection.php).

##### Example

Below, this is how simple it is to get your middleware stack running:

```php
<?php

use NoGlitchYo\MiddlewareCollectionRequestHandler\RequestHandler;
use NoGlitchYo\MiddlewareCollectionRequestHandler\Collection\SplStackMiddlewareCollection;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;

// Instantiate a collection of middleware.
$middlewareCollection = new SplStackMiddlewareCollection([
new class implements MiddlewareInterface{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface{
$handler->handle($request);
}
}
]);

// Instantiate a new request handler with a default handler and the middleware collection.
$requestHandler = RequestHandler::fromCallable(
function (ServerRequestInterface $serverRequest){
return new /* instance of ResponseInterface */;
},
$middlewareCollection
);

// Pass the request to the request handler.
$response = $requestHandler->handle(/* ServerRequestInterface */);

```

#### Create a custom MiddlewareCollectionInterface implementation

It is easy to create you own MiddlewareCollectionInterface implementation if you need. The interface requires only 3 methods:
```php
<?php
interface MiddlewareCollectionInterface
{
/**
* Must return true if there is no middleware in the collection to process.
* @return bool
*/
public function isEmpty(): bool;

/**
* Must return the next middleware to process in the collection.
* Depending on the implemented strategy, the middleware MAY not be removed from the collection.
* @return MiddlewareInterface
*/
public function next(): MiddlewareInterface;

/**
* Add a middleware instance of MiddlewareInterface to the collection.
*
* @param MiddlewareInterface $middleware
*
* @return MiddlewareCollectionInterface
*/
public function add(MiddlewareInterface $middleware): MiddlewareCollectionInterface;
}
```

#### Tests

Would like to the run the test suite? Go ahead:

`composer test`

### References
Expand All @@ -53,4 +125,4 @@ https://www.php-fig.org/psr/psr-15/

## License

This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
"scripts": {
"phpstan": "phpstan analyse -l max src",
"phpcs": "phpcs --standard=PSR2 ./src/",
"test": "phpunit phpunit.dist.xml"
"test": "phpunit -c phpunit.xml.dist"
}
}
13 changes: 12 additions & 1 deletion src/Collection/ArrayStackMiddlewareCollection.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types=1);
<?php
/**
* MIT License
*
Expand All @@ -22,15 +22,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);

namespace NoGlitchYo\MiddlewareCollectionRequestHandler\Collection;

use NoGlitchYo\MiddlewareCollectionRequestHandler\Exception\EmptyMiddlewareCollectionException;
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionInterface;
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionTrait;
use Psr\Http\Server\MiddlewareInterface;

class ArrayStackMiddlewareCollection implements MiddlewareCollectionInterface
{
use MiddlewareCollectionTrait;

/**
* @var MiddlewareInterface[]
*/
Expand Down Expand Up @@ -68,4 +72,11 @@ public function add(MiddlewareInterface $middleware): MiddlewareCollectionInterf

return $this;
}

public function addFromCallable(callable $callable): MiddlewareCollectionInterface
{
$this->add(self::createFromCallable($callable));

return $this;
}
}
13 changes: 12 additions & 1 deletion src/Collection/SplQueueMiddlewareCollection.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types=1);
<?php
/**
* MIT License
*
Expand All @@ -22,17 +22,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);

namespace NoGlitchYo\MiddlewareCollectionRequestHandler\Collection;

use NoGlitchYo\MiddlewareCollectionRequestHandler\Exception\EmptyMiddlewareCollectionException;
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionInterface;
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionTrait;
use Psr\Http\Server\MiddlewareInterface;
use RuntimeException;
use SplQueue;

class SplQueueMiddlewareCollection implements MiddlewareCollectionInterface
{
use MiddlewareCollectionTrait;

/**
* @var SplQueue
*/
Expand Down Expand Up @@ -73,4 +77,11 @@ public function add(MiddlewareInterface $middleware): MiddlewareCollectionInterf

return $this;
}

public function addFromCallable(callable $callable): MiddlewareCollectionInterface
{
$this->add(self::createFromCallable($callable));

return $this;
}
}
13 changes: 12 additions & 1 deletion src/Collection/SplStackMiddlewareCollection.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types=1);
<?php
/**
* MIT License
*
Expand All @@ -22,17 +22,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);

namespace NoGlitchYo\MiddlewareCollectionRequestHandler\Collection;

use NoGlitchYo\MiddlewareCollectionRequestHandler\Exception\EmptyMiddlewareCollectionException;
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionInterface;
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionTrait;
use Psr\Http\Server\MiddlewareInterface;
use RuntimeException;
use SplStack;

class SplStackMiddlewareCollection implements MiddlewareCollectionInterface
{
use MiddlewareCollectionTrait;

/**
* @var SplStack
*/
Expand Down Expand Up @@ -73,4 +77,11 @@ public function next(): MiddlewareInterface
throw new EmptyMiddlewareCollectionException();
}
}

public function addFromCallable(callable $callable): MiddlewareCollectionInterface
{
$this->add(self::createFromCallable($callable));

return $this;
}
}
19 changes: 18 additions & 1 deletion src/MiddlewareCollectionInterface.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types=1);
<?php
/**
* MIT License
*
Expand All @@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);

namespace NoGlitchYo\MiddlewareCollectionRequestHandler;

Expand All @@ -35,9 +36,25 @@
*/
interface MiddlewareCollectionInterface
{
/**
* Must return true if there is no middleware in the collection to process.
* @return bool
*/
public function isEmpty(): bool;

/**
* Must return the next middleware to process in the collection.
* Depending on the implemented strategy, the middleware MAY not be removed from the collection.
* @return MiddlewareInterface
*/
public function next(): MiddlewareInterface;

/**
* Add a middleware instance of MiddlewareInterface to the collection.
*
* @param MiddlewareInterface $middleware
*
* @return MiddlewareCollectionInterface
*/
public function add(MiddlewareInterface $middleware): MiddlewareCollectionInterface;
}
55 changes: 55 additions & 0 deletions src/MiddlewareCollectionTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* MIT License
*
* Copyright (c) 2019 Maxime Elomari
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);

namespace NoGlitchYo\MiddlewareCollectionRequestHandler;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

trait MiddlewareCollectionTrait
{
private static function createFromCallable(callable $callable): MiddlewareInterface
{
return new class($callable) implements MiddlewareInterface
{
private $callable;

public function __construct(callable $callable)
{
$this->callable = $callable;
}

public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
return call_user_func($this->callable, $request, $handler);
}
};
}
}
23 changes: 4 additions & 19 deletions src/RequestHandler.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types=1);
<?php
/**
* MIT License
*
Expand All @@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);

namespace NoGlitchYo\MiddlewareCollectionRequestHandler;

Expand All @@ -31,6 +32,8 @@

class RequestHandler implements RequestHandlerInterface
{
use RequestHandlerTrait;

/**
* @var MiddlewareCollectionInterface
*/
Expand Down Expand Up @@ -72,22 +75,4 @@ public function handle(ServerRequestInterface $request): ResponseInterface

return $nextMiddleware->process($request, $this);
}

private static function createRequestHandlerFromCallable(callable $callable): RequestHandlerInterface
{
return new class($callable) implements RequestHandlerInterface
{
private $callable;

public function __construct(callable $callable)
{
$this->callable = $callable;
}

public function handle(ServerRequestInterface $request): ResponseInterface
{
return call_user_func($this->callable, $request);
}
};
}
}
Loading

0 comments on commit 0d87530

Please sign in to comment.