DispatchStack calls $next only when the stack runs to the end.
This commit is contained in:
parent
26a6a25d3b
commit
297e985e84
|
|
@ -5,6 +5,9 @@ namespace WellRESTed\Dispatching;
|
|||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Dispatches an ordered sequence of middleware.
|
||||
*/
|
||||
class DispatchStack implements DispatchStackInterface
|
||||
{
|
||||
private $stack;
|
||||
|
|
@ -22,8 +25,6 @@ class DispatchStack implements DispatchStackInterface
|
|||
/**
|
||||
* Push a new middleware onto the stack.
|
||||
*
|
||||
* This method MUST preserve the order in which middleware added.
|
||||
*
|
||||
* @param mixed $middleware Middleware to dispatch in sequence
|
||||
* @return self
|
||||
*/
|
||||
|
|
@ -36,17 +37,17 @@ class DispatchStack implements DispatchStackInterface
|
|||
/**
|
||||
* Dispatch the contained middleware in the order in which they were added.
|
||||
*
|
||||
* The first middleware added to the stack MUST be the first to be
|
||||
* dispatched.
|
||||
* The first middleware that was added is dispatched first.
|
||||
*
|
||||
* Each middleware, when dispatched, MUST receive a $next callable that
|
||||
* dispatches the middleware that follows it. The only exception to this is
|
||||
* the last middleware in the stack which much receive a $next callable the
|
||||
* returns the response unchanged.
|
||||
* Each middleware, when dispatched, receives a $next callable that, when
|
||||
* called, will dispatch the next middleware in the sequence.
|
||||
*
|
||||
* If the instance is dispatched with no middleware added, the instance
|
||||
* MUST call $next passing $request and $response and return the returned
|
||||
* response.
|
||||
* When the stack is dispatched empty, or when all middleware in the stack
|
||||
* call the $next argument they were passed, this method will call the
|
||||
* $next it receieved.
|
||||
*
|
||||
* When any middleware in the stack returns a response without calling its
|
||||
* $next, the stack will not call the $next it received.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
|
|
@ -54,20 +55,16 @@ class DispatchStack implements DispatchStackInterface
|
|||
* @return ResponseInterface
|
||||
*/
|
||||
public function dispatch(ServerRequestInterface $request, ResponseInterface $response, $next)
|
||||
{
|
||||
$chain = $this->getCallableChain();
|
||||
$response = $chain($request, $response);
|
||||
return $next($request, $response);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
private function getCallableChain()
|
||||
{
|
||||
$dispatcher = $this->dispatcher;
|
||||
|
||||
// No-op function to use as the final middleware's $mext.
|
||||
$next = function ($request, $response) {
|
||||
// This flag will be set to true when the last middleware calls $next.
|
||||
$stackCompleted = false;
|
||||
|
||||
// The final middleware's $next returns $response unchanged and sets
|
||||
// the $stackCompleted flag to indicate the stack has completed.
|
||||
$chain = function ($request, $response) use (&$stackCompleted) {
|
||||
$stackCompleted = true;
|
||||
return $response;
|
||||
};
|
||||
|
||||
|
|
@ -77,11 +74,17 @@ class DispatchStack implements DispatchStackInterface
|
|||
// contain a dispatcher, the associated middleware, and a $next
|
||||
// that is the links to the next middleware in the chain.
|
||||
foreach (array_reverse($this->stack) as $middleware) {
|
||||
$next = function ($request, $response) use ($dispatcher, $middleware, $next) {
|
||||
return $dispatcher->dispatch($middleware, $request, $response, $next);
|
||||
$chain = function ($request, $response) use ($dispatcher, $middleware, $chain) {
|
||||
return $dispatcher->dispatch($middleware, $request, $response, $chain);
|
||||
};
|
||||
}
|
||||
|
||||
return $next;
|
||||
$response = $chain($request, $response);
|
||||
|
||||
if ($stackCompleted) {
|
||||
return $next($request, $response);
|
||||
} else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,16 @@ use Psr\Http\Message\ResponseInterface;
|
|||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use WellRESTed\MiddlewareInterface;
|
||||
|
||||
/**
|
||||
* Dispatches an ordered sequence of middleware.
|
||||
*/
|
||||
interface DispatchStackInterface extends MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Push a new middleware onto the stack.
|
||||
*
|
||||
* This method MUST preserve the order in which middleware added.
|
||||
*
|
||||
* @param mixed $middleware Middleware to dispatch in sequence
|
||||
* @return self
|
||||
*/
|
||||
|
|
@ -19,16 +24,24 @@ interface DispatchStackInterface extends MiddlewareInterface
|
|||
/**
|
||||
* Dispatch the contained middleware in the order in which they were added.
|
||||
*
|
||||
* The first middleware added to the stack is the first to be dispatched.
|
||||
* The first middleware added to the stack MUST be dispatched first.
|
||||
*
|
||||
* Each middleware, when dispatched, will receive a $next callable that
|
||||
* dispatches the middleware that follows it. The only exception to this is
|
||||
* the last middleware in the stack which much receive a $next callable the
|
||||
* Each middleware, when dispatched, MUST receive a $next callable that
|
||||
* dispatches the middleware that follows it, unless it is the last
|
||||
* middleware. The last middleware MUST receive a $next callable that
|
||||
* returns the response unchanged.
|
||||
*
|
||||
* If the instance is dispatched with no middleware added, the instance
|
||||
* MUST call $next passing $request and $response and return the returned
|
||||
* response.
|
||||
* When any middleware does not call the $next argument it recieved, the
|
||||
* stack instance MUST stop propogating through the stack and MUST return
|
||||
* the response without calling the $next argument passed to dispatch.
|
||||
*
|
||||
* This method MUST call the passed $next argument when:
|
||||
* - The stack is empty (i.e., there is no middleware to dispatch)
|
||||
* - Each middleware called the $next that it receieved.
|
||||
*
|
||||
* This method MUST NOT call the passed $next argument when the stack is
|
||||
* not empty and any middleware returns a response without calling the
|
||||
* $next it receieved.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ class DispatchStackTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
* @covers ::getCallableChain
|
||||
*/
|
||||
public function testDispachesMiddlewareInOrderAdded()
|
||||
{
|
||||
|
|
@ -76,6 +75,22 @@ class DispatchStackTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(["first", "second", "third"], $callOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
*/
|
||||
public function testCallsNextAfterDispatchingEmptyStack()
|
||||
{
|
||||
$nextCalled = false;
|
||||
$next = function ($request, $response) use (&$nextCalled) {
|
||||
$nextCalled = true;
|
||||
return $response;
|
||||
};
|
||||
|
||||
$stack = new DispatchStack($this->dispatcher->reveal());
|
||||
$stack->dispatch($this->request->reveal(), $this->response->reveal(), $next);
|
||||
$this->assertTrue($nextCalled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::dispatch
|
||||
*/
|
||||
|
|
@ -103,7 +118,7 @@ class DispatchStackTest extends \PHPUnit_Framework_TestCase
|
|||
/**
|
||||
* @covers ::dispatch
|
||||
*/
|
||||
public function testCallsNextAfterDispatchingEmptyStack()
|
||||
public function testDoesNotCallNextWhenStackStopsEarly()
|
||||
{
|
||||
$nextCalled = false;
|
||||
$next = function ($request, $response) use (&$nextCalled) {
|
||||
|
|
@ -111,8 +126,19 @@ class DispatchStackTest extends \PHPUnit_Framework_TestCase
|
|||
return $response;
|
||||
};
|
||||
|
||||
$middlewareGo = function ($request, $response, $next) use (&$callOrder) {
|
||||
return $next($request, $response);
|
||||
};
|
||||
$middlewareStop = function ($request, $response, $next) use (&$callOrder) {
|
||||
return $response;
|
||||
};
|
||||
|
||||
$stack = new DispatchStack($this->dispatcher->reveal());
|
||||
$stack->add($middlewareGo);
|
||||
$stack->add($middlewareStop);
|
||||
$stack->add($middlewareStop);
|
||||
|
||||
$stack->dispatch($this->request->reveal(), $this->response->reveal(), $next);
|
||||
$this->assertTrue($nextCalled);
|
||||
$this->assertFalse($nextCalled);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue