From 0f9c5079f9935afaf9774fcd36042927c6e08285 Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Sun, 10 May 2015 18:58:08 -0400 Subject: [PATCH] Add Server --- src/Server.php | 146 +++++++++++++++++++++++++++++ test/tests/unit/ServerTest.php | 164 +++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 src/Server.php create mode 100644 test/tests/unit/ServerTest.php diff --git a/src/Server.php b/src/Server.php new file mode 100644 index 0000000..42bcf0d --- /dev/null +++ b/src/Server.php @@ -0,0 +1,146 @@ +dispatcher = $this->getDispatcher(); + $this->stack = []; + } + + /** + * Push a new middleware onto the stack. + * + * @param mixed $middleware Middleware to dispatch in sequence + * @return self + */ + public function add($middleware) + { + $this->stack[] = $middleware; + return $this; + } + + /** + * 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. + * + * 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 + * 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. + * + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callable $next + * @return ResponseInterface + */ + public function dispatch(ServerRequestInterface $request, ResponseInterface $response, $next) + { + return $this->dispatcher->dispatch($this->stack, $request, $response, $next); + } + + // ------------------------------------------------------------------------ + + /** + * Return a new Router that uses the server's dispatcher. + * + * @return Router + */ + public function makeRouter() + { + return new Router($this->dispatcher); + } + + /** + * Perform the request-response cycle. + * + * This method reads a server request, dispatches the request through the + * server's stack of middleware, and outputs the response. + */ + public function respond() + { + $request = $this->getRequest(); + $response = $this->getResponse(); + $next = function ($request, $response) { + return $response; + }; + $response = $this->dispatch($request, $response, $next); + $responder = $this->getResponder(); + $responder->respond($request, $response); + } + + // ------------------------------------------------------------------------ + // The following method provide instances using default classes. To use + // custom classes, subclass Server and override methods as needed. + + /** + * Return an instance to dispatch middleware. + * + * @return DispatcherInterface + */ + protected function getDispatcher() + { + return new Dispatcher(); + } + + // @codeCoverageIgnoreStart + + /** + * Return an instance representing the request submitted to the server. + * + * @return ServerRequestInterface + */ + protected function getRequest() + { + return ServerRequest::getServerRequest(); + } + + /** + * Return an instance that will output the response to the client. + * + * @return ResponderInterface + */ + protected function getResponder() + { + return new Responder(); + } + + /** + * Return a "blank" response instance to populate. + * + * The response will be dispatched through the middleware and eventually + * output to the client. + * + * @return ResponseInterface + */ + protected function getResponse() + { + return new Response(); + } + + // @codeCoverageIgnoreEnd +} diff --git a/test/tests/unit/ServerTest.php b/test/tests/unit/ServerTest.php new file mode 100644 index 0000000..0271559 --- /dev/null +++ b/test/tests/unit/ServerTest.php @@ -0,0 +1,164 @@ +request = $this->prophesize('Psr\Http\Message\ServerRequestInterface'); + $this->response = $this->prophesize('Psr\Http\Message\ResponseInterface'); + $this->responder = $this->prophesize('WellRESTed\Responder\ResponderInterface'); + $this->responder->respond(Argument::cetera())->willReturn(); + $this->dispatcher = $this->prophesize('WellRESTed\Dispatching\DispatcherInterface'); + $this->dispatcher->dispatch(Argument::cetera())->will( + function ($args) { + list($middleware, $request, $response, $next) = $args; + return $next($request, $response); + } + ); + + $this->server = $this->getMockBuilder('WellRESTed\Server') + ->setMethods(["getDispatcher", "getRequest", "getResponse", "getResponder"]) + ->disableOriginalConstructor() + ->getMock(); + $this->server->expects($this->any()) + ->method("getDispatcher") + ->will($this->returnValue($this->dispatcher->reveal())); + $this->server->expects($this->any()) + ->method("getRequest") + ->will($this->returnValue($this->request->reveal())); + $this->server->expects($this->any()) + ->method("getResponse") + ->will($this->returnValue($this->response->reveal())); + $this->server->expects($this->any()) + ->method("getResponder") + ->will($this->returnValue($this->responder->reveal())); + $this->server->__construct(); + } + + /** + * @covers ::__construct + * @covers ::getDispatcher + * @uses WellRESTed\Dispatching\Dispatcher + */ + public function testCreatesInstances() + { + $server = new Server(); + $this->assertNotNull($server); + } + + /** + * @covers ::add + */ + public function testAddIsFluid() + { + $server = new Server(); + $this->assertSame($server, $server->add("middleware")); + } + + /** + * @covers ::add + * @covers ::dispatch + */ + public function testDispatchesMiddlewareStack() + { + $next = function ($request, $response) { + return $response; + }; + + $this->server->add("first"); + $this->server->add("second"); + $this->server->add("third"); + + $this->server->dispatch($this->request->reveal(), $this->response->reveal(), $next); + + $this->dispatcher->dispatch( + ["first", "second", "third"], + $this->request->reveal(), + $this->response->reveal(), + $next + )->shouldHaveBeenCalled(); + } + + // ------------------------------------------------------------------------ + // Respond + + /** + * @covers ::respond + */ + public function testRespondDispatchesRequest() + { + $this->server->respond(); + $this->dispatcher->dispatch( + Argument::any(), + $this->request->reveal(), + Argument::any(), + Argument::any() + )->shouldHaveBeenCalled(); + } + + /** + * @covers ::respond + */ + public function testRespondDispatchesResponse() + { + $this->server->respond(); + $this->dispatcher->dispatch( + Argument::any(), + Argument::any(), + $this->response->reveal(), + Argument::any() + )->shouldHaveBeenCalled(); + } + + /** + * @covers ::respond + */ + public function testRespondSendsResponseToResponder() + { + $this->server->respond(); + $this->responder->respond( + $this->request->reveal(), + $this->response->reveal() + )->shouldHaveBeenCalled(); + } + + // ------------------------------------------------------------------------ + // Router + + public function testCreatesRouterWithDispatcher() + { + $this->request->getMethod()->willReturn("GET"); + $this->request->getRequestTarget()->willReturn("/"); + + $next = function ($request, $response) { + return $response; + }; + + $router = $this->server->makeRouter(); + $router->register("GET", "/", "middleware"); + $router->dispatch($this->request->reveal(), $this->response->reveal(), $next); + + $this->dispatcher->dispatch( + "middleware", + $this->request->reveal(), + $this->response->reveal(), + $next + )->shouldHaveBeenCalled(); + } +}