Created
May 18, 2016 20:43
-
-
Save dlundgren/37d11d030267e98c3bcf1f5dd9aaa64c to your computer and use it in GitHub Desktop.
Middleware Stack with PSR-7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Contains \dlundgren\MiddlewareStack\PriorityQueue | |
*/ | |
namespace dlundgren\MiddlewareStack; | |
use dlundgren\MiddlewareStack\PriorityQueue; | |
use Psr\Http\Message\ServerRequestInterface; | |
/** | |
* Middleware Stack | |
* | |
* @package dlundgren\MiddlewareStack | |
*/ | |
class MiddlewareStack | |
{ | |
const EARLY = 100; | |
const NORMAL = 0; | |
const LATE = -100; | |
/** | |
* List of middleware | |
* | |
* @var PriorityQueue | |
*/ | |
private $stack; | |
/** | |
* Whether or not the stack is locked | |
* | |
* @var bool | |
*/ | |
private $locked = false; | |
public function __construct() | |
{ | |
$this->stack = new PriorityQueue(); | |
} | |
/** | |
* Adds the middleware to the stack at the given priority | |
* | |
* @param callable $callable | |
* @param int $priority | |
*/ | |
public function add(callable $callable, $priority = self::NORMAL) | |
{ | |
if ($this->locked) { | |
throw new \RuntimeException("Middleware can't be added once the stack is dequeing"); | |
} | |
$this->stack->after($callable, $priority); | |
} | |
/** | |
* Allows to retrieve the stack | |
* | |
* @seam | |
* @return PriorityQueue | |
*/ | |
public function stack() | |
{ | |
return $this->stack; | |
} | |
/** | |
* Resolves the Middlware Stack out so it can be run | |
* | |
* @param callable $app | |
* @return \Closure | |
*/ | |
public function resolve(callable $app) | |
{ | |
$this->locked = true; | |
$next = $app; | |
$this->stack->setReverseQueues(true); | |
foreach ($this->stack as $mw) { | |
$next = function (ServerRequestInterface $request) use ($mw, $next) { | |
return $mw($request, $next); | |
}; | |
} | |
$this->stack->setReverseQueues(false); | |
return $next; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Contains \dlundgren\MiddlewareStack\PriorityQueue | |
*/ | |
namespace NetId\Application\Shared\Utility; | |
// PHP < 5.6 can't use constant expressions | |
define('PRIORITY_QUEUE_MODE_NORMAL', \SplDoublyLinkedList::IT_MODE_FIFO | \SplDoublyLinkedList::IT_MODE_KEEP); | |
define('PRIORITY_QUEUE_MODE_REVERSE', \SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_KEEP); | |
/** | |
* Priority Queue | |
* | |
* Unlike SplPriorityQueue this does not empty itself. Making it more easily reusable | |
* | |
* @package dlundgren\MiddlewareStack | |
*/ | |
class PriorityQueue | |
implements \Countable, \IteratorAggregate | |
{ | |
const AFTER = 1; | |
const BEFORE = -1; | |
const MODE_NORMAL = PRIORITY_QUEUE_MODE_NORMAL; | |
const MODE_REVERSE = PRIORITY_QUEUE_MODE_REVERSE; | |
/** | |
* List of values | |
* | |
* @var array[<priority>][] | |
*/ | |
private $values = []; | |
/** | |
* How many items are in the queue | |
* | |
* @var int | |
*/ | |
private $count = 0; | |
/** | |
* Whether the queues should be reversed | |
* | |
* @var bool | |
*/ | |
private $reverseQueues = false; | |
/** | |
* Inserts the value into the queue | |
* | |
* @param $value | |
* @param $priority | |
*/ | |
private function insert($value, $priority, $position) | |
{ | |
if (!isset($this->values[$priority])) { | |
$queue = new \SplDoublyLinkedList(); | |
$queue->setIteratorMode(self::MODE_NORMAL); | |
$this->values[$priority] = $queue; | |
krsort($this->values); | |
} | |
if ($position === self::AFTER) { | |
$this->values[$priority]->push($value); | |
} | |
else { | |
$this->values[$priority]->unshift($value); | |
} | |
$this->count++; | |
} | |
/** | |
* Inserts the value at the beginning of the given priority queue | |
* | |
* @param $value | |
* @param $priority | |
*/ | |
public function before($value, $priority) | |
{ | |
$this->insert($value, $priority, self::BEFORE); | |
} | |
/** | |
* Inserts the value at the end of the given priority queue | |
* | |
* @param $value | |
* @param $priority | |
*/ | |
public function after($value, $priority) | |
{ | |
$this->insert($value, $priority, self::AFTER); | |
} | |
/** | |
* Returns a count of the number of items in the queue | |
* | |
* @param int|null $priority The priority to count. Default is all | |
* @return int | |
*/ | |
public function count($priority = null) | |
{ | |
return $priority === null ? $this->count : $this->values[$priority]->count(); | |
} | |
/** | |
* Sets whether or not the queues should be reversed | |
* | |
* @param bool $reverse | |
*/ | |
public function setReverseQueues($reverse = false) | |
{ | |
$this->reverseQueues = $reverse; | |
} | |
/** | |
* Generates the values | |
* | |
* @return \Generator | |
*/ | |
public function getIterator() | |
{ | |
foreach ($this->values as $queue) { | |
/** @var \SplDoublyLinkedList $queue */ | |
$queue->setIteratorMode($this->reverseQueues ? PRIORITY_QUEUE_MODE_REVERSE : PRIORITY_QUEUE_MODE_NORMAL); | |
foreach ($queue as $value) { | |
yield $value; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment