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