Introduce FrontController as the replacement for RootController. api-2
authorRobert Sesek <rsesek@bluestatic.org>
Sun, 31 May 2015 20:23:31 +0000 (16:23 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sun, 31 May 2015 20:40:25 +0000 (16:40 -0400)
The FrontController has no delegate, but instead uses Interceptors to allow
middleware to further process the request or to interrupt control flow.

One integral change is that if no Action is found for the Request in the UrlMap,
then no action will be taken by the FrontController. Instead, a
NotFoundInterceptor should be used to send a HTTP 404 response in this event.

15 files changed:
data/controller.php
http/action.php
http/action_controller.php
http/front_controller.php [moved from http/root_controller.php with 69% similarity]
http/interceptor.php [new file with mode: 0644]
http/not_found_interceptor.php [new file with mode: 0644]
http/output_filter.php
http/rest_adapter.php
http/url_map.php
testing/tests/http/action_controller_test.php
testing/tests/http/front_controller_test.php [moved from testing/tests/http/root_controller_test.php with 65% similarity]
testing/tests/http/output_filter_test.php
testing/tests/http/rest_action_test.php
testing/tests/http/rest_adapter_test.php
testing/tests/http/url_map_test.php

index 79c338b79a14c2b2e4c4602535c8ec929bb36d6d..969c4cfe7f9143f250d730e93658eaa600e8fc80 100644 (file)
@@ -34,11 +34,13 @@ abstract class Controller extends http\RestAction
   protected $model = NULL;
 
   /*! Selects the Model object. */
-  public function FilterRequest(http\Request $request, http\Response $response)
+  public function Invoke(http\Request $request, http\Response $response)
   {
     $this->model = $this->_SelectModel();
     $this->model->SetFrom(array_merge(
         $request->data, $request->data['_POST'], $request->data['_GET']));
+
+    parent::Invoke($request, $response);
   }
 
   /*! Returns a new instance of the Model that this object will control. */
index 46f1859d5cf644788cdf7c3487fbf4ae6f717be5..0d8dd6348b37748447db037cacc7d7de8ab5bff9 100644 (file)
@@ -1,11 +1,11 @@
 <?php
 // Hoplite
 // Copyright (c) 2011 Blue Static
-// 
+//
 // This program is free software: you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by the Free
 // Software Foundation, either version 3 of the License, or any later version.
-// 
+//
 // This program is distributed in the hope that it will be useful, but WITHOUT
 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
@@ -22,33 +22,23 @@ namespace hoplite\http;
 */
 abstract class Action
 {
-  /*! \var RootController */
+  /*! \var FrontController */
   private $controller;
 
   /*!
-    Creates a new action with a reference to the RootController.
-    @param RootController
+    Creates a new action with a reference to the FrontController.
+    @param FrontController
   */
   public function __construct($controller)
   {
     $this->controller = $controller;
   }
 
-  /*! Accesses the RootController */
+  /*! Accesses the FrontController */
   public function controller() { return $this->controller; }
 
-  /*!
-    Called before the Action is Invoked().
-  */
-  public function FilterRequest(Request $request, Response $response) {}
-
   /*!
     Performs the action and fills out the response's data model.
   */
   public abstract function Invoke(Request $request, Response $response);
-
-  /*!
-    Called after this has been Invoked().
-  */
-  public function FilterResponse(Request $request, Response $response) {}
 }
index 3da7039bbbf8c61fa0ffa5a3cf1b0e119e4ecb58..25a4b3493be8f55cdf677d50cfc57676d7ea58c8 100644 (file)
@@ -34,8 +34,7 @@ class ActionController extends Action
   {
     $method = $this->_GetActionMethod($request);
     if (!method_exists($this, $method)) {
-      $response->response_code = ResponseCode::NOT_FOUND;
-      $this->controller()->Stop();
+      $this->controller()->SendResponseCode(ResponseCode::NOT_FOUND);
       return;
     }
 
similarity index 69%
rename from http/root_controller.php
rename to http/front_controller.php
index 75a25d7dcd85ccf7ade032776fc683684f42d428..b84f29851247d4b08e130ed00b21ed07726500b4 100644 (file)
@@ -1,11 +1,11 @@
 <?php
 // Hoplite
-// Copyright (c) 2011 Blue Static
-// 
+// Copyright (c) 2015 Blue Static
+//
 // This program is free software: you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by the Free
 // Software Foundation, either version 3 of the License, or any later version.
-// 
+//
 // This program is distributed in the hope that it will be useful, but WITHOUT
 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 
 namespace hoplite\http;
 
+require_once HOPLITE_ROOT . '/http/interceptor.php';
 require_once HOPLITE_ROOT . '/http/request.php';
 require_once HOPLITE_ROOT . '/http/response.php';
 require_once HOPLITE_ROOT . '/http/response_code.php';
 
 /*!
-  The RootController is meant to be invoked from the index.php of the
+  The FrontController is meant to be invoked from the index.php of the
   application.
 */
-class RootController
+class FrontController
 {
   /*! @var Request */
   private $request = NULL;
@@ -38,15 +39,13 @@ class RootController
   /*! @var OutputFilter */
   private $output_filter = NULL;
 
-  /*! @var RootControllerDelegate */
-  private $delegate = NULL;
+  /*! @var Array<Interceptor> */
+  private $interceptors = [];
 
   /*!
     Creates the controller with the request context information, typicallhy
     from the global scope ($GLOBALS), but can be injected for testing.
-    @param UrlMap The routing map
-    @param OutputFilter The object responsible for decorating output.
-    @param array& PHP globals array
+    @param array PHP globals array
   */
   public function __construct(array $globals)
   {
@@ -73,21 +72,18 @@ class RootController
     $this->output_filter = $output_filter;
   }
 
-  /*! Sets the delegate. */
-  public function set_delegate($delegate)
-  {
-    $this->delegate = $delegate;
-  }
-  public function delegate()
+  /*! Registers an Interceptor that will be run before executing an Action. */
+  public function AddInterceptor(Interceptor $interceptor)
   {
-    return $this->delegate;
+    $this->interceptors[] = $interceptor;
   }
 
   /*!
-    Createst the Request and Response that are used throughout the duration of
-    the execution.
-  */
-  public function Run()
+    Called in index.php to process the current HTTP request. This initializes the
+    Request object from its data and then routes the request to generate the
+    response.
+   */
+  public function ProcessRequest()
   {
     // The query rewriter module of the webserver rewrites a request from:
     //    http://example.com/webapp/user/view/42
@@ -113,25 +109,20 @@ class RootController
     // Register self as the active instance.
     $GLOBALS[__CLASS__] = $this;
 
-    if ($this->delegate)
-      $this->delegate->OnInitialRequest($this->request, $this->response);
-
     // Dispatch the request to an Action.
     $this->RouteRequest($this->request);
 
     // When control returns here, all actions have been invoked and it's time
     // to start the output filter and exit.
-    $this->Stop();
+    $this->SendResponse();
   }
 
   /*!
     Prevents any other Actions from executing. This starts the OutputFilter and
     then exits.
   */
-  public function Stop()
+  public function SendResponse()
   {
-    if ($this->delegate)
-      $this->delegate->WillStop($this->request, $this->response);
     $this->output_filter->FilterOutput($this->request, $this->response);
     $this->_Exit();
   }
@@ -139,10 +130,10 @@ class RootController
   /*!
     Sets the response code and stops the controller. Returns void.
   */
-  public function StopWithCode($code)
+  public function SendResponseCode($code)
   {
     $this->response->response_code = $code;
-    $this->Stop();
+    $this->SendResponse();
   }
 
   /*!
@@ -150,13 +141,12 @@ class RootController
     location.
     @param string The destination location of the redirect.
   */
-  public function StopWithRedirect($location)
+  public function SendResponseRedirect($location)
   {
     $this->response->headers['Location'] = $location;
-    $this->StopWithCode(ResponseCode::FOUND);
+    $this->SendResponseCode(ResponseCode::FOUND);
   }
 
-
   /*!
     Wrapper around PHP exit().
   */
@@ -166,45 +156,24 @@ class RootController
   }
 
   /*!
-    Invoked by Run() and can be invoked by others to evaluate and perform the
-    lookup in the UrlMap. This then calls InvokeAction().
+    Given an Request object, this executes the action for the corresponding URL.
+    The action is located by performing a lookup in the UrlMap. Interceptors
+    are run before invoking the action.
     @param Request The Request whose URL will be routed
   */
   public function RouteRequest(Request $request)
   {
-    if ($this->delegate)
-      $this->delegate->WillRouteRequest($request, $this->response);
-
     $url_map_value = $this->url_map->Evaluate($request);
 
     $action = NULL;
     if ($url_map_value)
       $action = $this->url_map->LookupAction($url_map_value);
 
-    if (!$action) {
-      $this->response->response_code = ResponseCode::NOT_FOUND;
-      $this->Stop();
-      return;
-    }
+    foreach ($this->interceptors as $interceptor)
+      $interceptor->DoIntercept($this, $action, $request, $this->response);
 
-    $this->InvokeAction($action);
-  }
-
-  /*!
-    Used to run an Action and drive it through its states.
-    @param Action
-  */
-  public function InvokeAction(Action $action)
-  {
-    if ($this->delegate)
-      $this->delegate->WillInvokeAction($action, $this->request, $this->response);
-
-    $action->FilterRequest($this->request, $this->response);
-    $action->Invoke($this->request, $this->response);
-    $action->FilterResponse($this->request, $this->response);
-
-    if ($this->delegate)
-      $this->delegate->DidInvokeAction($action, $this->request, $this->response);
+    if ($action)
+      $action->Invoke($request, $this->response);
   }
 
   /*!
@@ -258,19 +227,3 @@ class RootController
     return $url . $new_path;
   }
 }
-
-/*!
-  Delegate for the root controller. All methods are optional.
-*/
-interface RootControllerDelegate
-{
-  public function OnInitialRequest(Request $request, Response $response);
-
-  public function WillRouteRequest(Request $request, Response $response);
-
-  public function WillInvokeAction(Action $action, Request $request, Response $response);
-
-  public function DidInvokeAction(Action $action, Request $request, Response $response);
-
-  public function WillStop(Request $request, Response $response);
-}
diff --git a/http/interceptor.php b/http/interceptor.php
new file mode 100644 (file)
index 0000000..15bf0ce
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+// Hoplite
+// Copyright (c) 2015 Blue Static
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+namespace hoplite\http;
+
+/*!
+  An Interceptor runs as part of FrontController::RouteRequest() to provide
+  extra processing before the Action is Invoke()ed. The Action is provided to
+  allow the Interceptor to change the control flow based on characteristics
+  of the Action processor. An Interceptor may interrupt the control flow by
+  calling SendResponse() on the controller.
+*/
+interface Interceptor
+{
+  /*!
+    Performs the action and fills out the response's data model.
+  */
+  public function DoIntercept(FrontController $controller,
+                              Action $action = NULL,
+                              Request $request,
+                              Response $response);
+}
diff --git a/http/not_found_interceptor.php b/http/not_found_interceptor.php
new file mode 100644 (file)
index 0000000..a49fd41
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+// Hoplite
+// Copyright (c) 2015 Blue Static
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+namespace hoplite\http;
+
+require_once HOPLITE_ROOT . '/http/interceptor.php';
+require_once HOPLITE_ROOT . '/http/response_code.php';
+
+class NotFoundInterceptor implements Interceptor
+{
+  public function DoIntercept(FrontController $controller,
+                              Action $action,
+                              Request $request,
+                              Response $response)
+  {
+    if ($action === NULL) {
+      $controller->SendResponseCode(ResponseCode::NOT_FOUND);
+    }
+  }
+}
index 1e1925abf0f99c120e9391b74d8cb5bac7da4eba..1ae998f37f9df0acadfc0431aa52ad787d35ba64 100644 (file)
@@ -29,7 +29,7 @@ require_once HOPLITE_ROOT . '/views/template_loader.php';
 */
 class OutputFilter
 {
-  /*! @var RootController */
+  /*! @var FrontController */
   private $controller;
 
   /*! @var WeakInterface<OutputFilterDelegate> */
@@ -52,15 +52,15 @@ class OutputFilter
   const RENDER_TEMPLATE = 'template';
 
   /*!
-    Constructor that takes a reference to the RootController.
+    Constructor that takes a reference to the FrontController.
   */
-  public function __construct(RootController $controller)
+  public function __construct(FrontController $controller)
   {
     $this->controller = $controller;
     $this->delegate = new \hoplite\base\WeakInterface('hoplite\http\OutputFilterDelegate');
   }
 
-  /*! Accessor for the RootController. */
+  /*! Accessor for the FrontController. */
   public function controller() { return $this->controller; }
 
   /*! Accessors for the delegate. */
@@ -71,7 +71,7 @@ class OutputFilter
   public function delegate() { return $this->delegate->Get(); }
 
   /*! @brief Main entry point for output filtering
-    This is called from the RootController to begin processing the output and
+    This is called from the FrontController to begin processing the output and
     generating the response.
   */
   public function FilterOutput(Request $request, Response $response)
index 0329177f29bae0f0e73ab8ebc59ea5e866974c75..aeec994db0434bebeca82d3eddd3886a08e930e2 100644 (file)
@@ -29,9 +29,10 @@ abstract class RestAdapter extends ActionController
   /*! @var RestAction The RESTful interface which will be adapted. */
   protected $action = NULL;
 
-  public function FilterRequest(Request $request, Response $response)
+  public function Invoke(Request $request, Response $response)
   {
     $this->action = $this->_GetRestAction();
+    parent::Invoke($request, $response);
   }
 
   /*! Gets the RestAction that will be adapted. */
index bc40a672e697578d75a23a914fe388306395ae0e..8bf450ae428f2a555a83d08fbc9f147575cadbac 100644 (file)
@@ -25,7 +25,7 @@ require_once HOPLITE_ROOT . '/base/functions.php';
 */
 class UrlMap
 {
-  /*! @var RootController */
+  /*! @var FrontController */
   private $controller;
 
   /*! @var array The map of URLs to actions. */
@@ -35,15 +35,15 @@ class UrlMap
   private $file_loader = NULL;
 
   /*!
-    Constructs the object with a reference to the RootController.
-    @param RootController
+    Constructs the object with a reference to the FrontController.
+    @param FrontController
   */
-  public function __construct(RootController $controller)
+  public function __construct(FrontController $controller)
   {
     $this->controller = $controller;
   }
 
-  /*! Accessor for the RootController */
+  /*! Accessor for the FrontController */
   public function controller() { return $this->controller; }
 
   /*! Gets the URL map */
@@ -55,7 +55,7 @@ class UrlMap
     The keys can be either a URL prefix or a regular expression.
 
     For URL prefixes, the pattern is matched relative to the root of the entry-
-    point as specified by the RootController. Patterns should not begin with a
+    point as specified by the FrontController. Patterns should not begin with a
     slash. Path fragment parameter extraction can be performed as well. For
     example, the pattern 'user/view/{id}' will match a URL like
       http://example.com/webapp/user/view/42
index 5781b97536fe05a9b6ecc0db244deeaf5a701f70..6e74c121db13132fa3e7e668219719f778112201 100644 (file)
@@ -34,7 +34,7 @@ class ActionControllerTest extends \PHPUnit_Framework_TestCase
   public function setUp()
   {
     $globals = array();
-    $this->fixture = new TestActionController(new http\RootController($globals));
+    $this->fixture = new TestActionController(new http\FrontController($globals));
     $this->request = new http\Request();
     $this->response = new http\Response();
   }
@@ -49,11 +49,11 @@ class ActionControllerTest extends \PHPUnit_Framework_TestCase
   public function testFailedDispatch()
   {
     $globals = array();
-    $mock = $this->getMock('hoplite\http\RootController', array(), array($globals));
+    $mock = $this->getMock('hoplite\http\FrontController', array('SendResponse'), array($globals));
     $this->fixture = new TestActionController($mock);
 
     $mock->expects($this->once())
-         ->method('Stop');
+         ->method('SendResponse');
 
     $this->request->data['action'] = 'nothing';
     $this->fixture->Invoke($this->request, $this->response);
similarity index 65%
rename from testing/tests/http/root_controller_test.php
rename to testing/tests/http/front_controller_test.php
index bd10a93fec76105936599d9642bb281497366937..e3a86bd613e6bf27cef4758d7af5f216d03396b8 100644 (file)
@@ -1,11 +1,11 @@
 <?php
 // Hoplite
-// Copyright (c) 2011 Blue Static
-// 
+// Copyright (c) 2015 Blue Static
+//
 // This program is free software: you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by the Free
 // Software Foundation, either version 3 of the License, or any later version.
-// 
+//
 // This program is distributed in the hope that it will be useful, but WITHOUT
 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
@@ -18,53 +18,65 @@ namespace hoplite\test;
 use hoplite\http as http;
 
 require_once HOPLITE_ROOT . '/http/action.php';
+require_once HOPLITE_ROOT . '/http/front_controller.php';
+require_once HOPLITE_ROOT . '/http/interceptor.php';
 require_once HOPLITE_ROOT . '/http/output_filter.php';
-require_once HOPLITE_ROOT . '/http/root_controller.php';
 require_once HOPLITE_ROOT . '/http/url_map.php';
 
 class ActionReporter extends http\Action
 {
-  public $did_filter_request = FALSE;
   public $did_invoke = FALSE;
-  public $did_filter_response = FALSE;
 
-  public function FilterRequest(http\Request $q, http\Response $s)
+  public function Invoke(http\Request $q, http\Response $s)
   {
-    $this->did_filter_request = TRUE;
+    $this->did_invoke = TRUE;
   }
+}
 
-  public function Invoke(http\Request $q, http\Response $s)
+class ClosureInterceptor implements http\Interceptor
+{
+  private $interceptor;
+  public $did_intercept = FALSE;
+
+  public function __construct($interceptor)
   {
-    $this->did_invoke = TRUE;
+    $this->interceptor = $interceptor;
   }
 
-  public function FilterResponse(http\Request $q, http\Response $s)
+  public function DoIntercept(http\FrontController $controller,
+                              http\Action $action = NULL,
+                              http\Request $request,
+                              http\Response $response)
   {
-    $this->did_filter_response = TRUE;
+    $this->did_intercept = TRUE;
+    if ($this->interceptor) {
+      $interceptor = $this->interceptor;
+      $interceptor($controller, $action, $request, $response);
+    }
   }
 }
 
-class RootControllerTest extends \PHPUnit_Framework_TestCase
+class FrontControllerTest extends \PHPUnit_Framework_TestCase
 {
   /*!
-    Configures a mock RootControler.
+    Configures a mock FrontControler.
     @param array|NULL Array of methods to mock
     @param varargs Constructor parameters.
-    @return Mock RootControler
+    @return Mock FrontControler
   */
   public function ConfigureMock()
   {
     $args = func_get_args();
-    return $this->getMock('hoplite\http\RootController', $args[0], array_slice($args, 1));
+    return $this->getMock('hoplite\http\FrontController', $args[0], array_slice($args, 1));
   }
 
-  public function testRun()
+  public function testProcessRequest()
   {
     $globals = array('_SERVER' => array(
         'REQUEST_METHOD' => 'GET',
         'PATH_INFO' => '/some/action/42'
     ));
-    $mock = $this->ConfigureMock(array('RouteRequest', 'Stop'), $globals);
+    $mock = $this->ConfigureMock(array('RouteRequest', 'SendResponse'), $globals);
 
     $mock->request()->url = 'some/action/42';
 
@@ -73,42 +85,20 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
          ->with($this->equalTo($mock->request()));
 
     $mock->expects($this->once())
-         ->method('Stop');
-
-    $mock->Run();
-  }
-
-  public function testInvokeAction()
-  {
-    $globals = array();
-    $fixture = new http\RootController($globals);
-
-    $action = new ActionReporter($fixture);
+         ->method('SendResponse');
 
-    $this->assertFalse($action->did_filter_request);
-    $this->assertFalse($action->did_invoke);
-    $this->assertFalse($action->did_filter_response);
-
-    $fixture->InvokeAction($action);
-
-    $this->assertTrue($action->did_filter_request);
-    $this->assertTrue($action->did_invoke);
-    $this->assertTrue($action->did_filter_response);
+    $mock->ProcessRequest();
   }
 
   public function testRouteRequest()
   {
     $globals = array();
-    $mock = $this->ConfigureMock(array('Stop', 'InvokeAction'), $globals);
+    $mock = $this->ConfigureMock(array('SendResponse', 'InvokeAction'), $globals);
 
     $mock->request()->url = 'some/action/42';
     $map_value = 'ActionReporter';
     $action = new ActionReporter($mock);
 
-    $mock->expects($this->once())
-         ->method('InvokeAction')
-         ->with($this->isInstanceOf('hoplite\test\ActionReporter'));
-
     $url_map = $this->getMock('hoplite\http\UrlMap', array(), array($mock));
     $url_map->expects($this->once())
             ->method('Evaluate')
@@ -121,17 +111,19 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
 
     $mock->set_url_map($url_map);
     $mock->RouteRequest($mock->request());
+
+    $this->assertTrue($action->did_invoke);
   }
 
   public function testRouteRequestInvalid()
   {
     $globals = array();
-    $mock = $this->ConfigureMock(array('Stop'), $globals);
+    $mock = $this->ConfigureMock(array('SendResponse'), $globals);
 
     $mock->request()->url = 'another/action';
 
-    $mock->expects($this->once())
-         ->method('Stop');
+    $mock->expects($this->never())
+         ->method('SendResponse');
 
     $url_map = $this->getMock('hoplite\http\UrlMap', array(), array($mock));
     $url_map->expects($this->once())
@@ -140,10 +132,10 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
 
     $mock->set_url_map($url_map);
     $mock->RouteRequest($mock->request());
-    $this->assertEquals(http\ResponseCode::NOT_FOUND, $mock->response()->response_code);
+    // Nothing should happen for a non-routed request.
   }
 
-  public function testStop()
+  public function testSendResponse()
   {
     $globals = array();
     $mock = $this->ConfigureMock(array('_Exit'), $globals);
@@ -158,7 +150,7 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
                          $this->isInstanceOf('hoplite\http\Response'));
 
     $mock->set_output_filter($output_filter);
-    $mock->Stop();
+    $mock->SendResponse();
   }
 
   public function testStopWithRedirect()
@@ -176,7 +168,7 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
 
     $mock->set_output_filter($output_filter);
 
-    $mock->StopWithRedirect('/foo/bar');
+    $mock->SendResponseRedirect('/foo/bar');
 
     $this->assertEquals('/foo/bar', $mock->response()->headers['Location']);
     $this->assertEquals(http\ResponseCode::FOUND, $mock->response()->response_code);
@@ -192,7 +184,7 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
         'SERVER_PORT' => 80,
       ),
     );
-    $mock = new \hoplite\http\RootController($globals);
+    $mock = new \hoplite\http\FrontController($globals);
 
     $this->assertEquals($mock->MakeURL('/'), '/hoplite/webapp/');
     $this->assertEquals($mock->MakeURL('/', TRUE), 'http://www.bluestatic.org/hoplite/webapp/');
@@ -202,7 +194,7 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
 
     $globals['_SERVER']['HTTPS'] = 'on';
     $globals['_SERVER']['SERVER_PORT'] = 443;
-    $mock = new \hoplite\http\RootController($globals);
+    $mock = new \hoplite\http\FrontController($globals);
     $this->assertEquals($mock->MakeURL('/'), '/hoplite/webapp/');
     $this->assertEquals($mock->MakeURL('/', TRUE), 'https://www.bluestatic.org/hoplite/webapp/');
 
@@ -210,13 +202,13 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
     $this->assertEquals($mock->MakeURL('/path/3', TRUE), 'https://www.bluestatic.org/hoplite/webapp/path/3');
 
     $globals['_SERVER']['SERVER_PORT'] = 8080;
-    $mock = new \hoplite\http\RootController($globals);
+    $mock = new \hoplite\http\FrontController($globals);
     $this->assertEquals($mock->MakeURL('/path/2'), '/hoplite/webapp/path/2');
     $this->assertEquals($mock->MakeURL('/', TRUE), 'https://www.bluestatic.org:8080/hoplite/webapp/');
     $this->assertEquals($mock->MakeURL('/path/3', TRUE), 'https://www.bluestatic.org:8080/hoplite/webapp/path/3');
   }
 
-  public function testAbsolutifyRoot()
+  public function testAbsolutifyFront()
   {
     $globals = array(
       '_SERVER' => array(
@@ -226,14 +218,14 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
         'SERVER_PORT' => 80,
       ),
     );
-    $mock = new \hoplite\http\RootController($globals);
+    $mock = new \hoplite\http\FrontController($globals);
 
     $this->assertEquals($mock->MakeURL('/'), '/hoplite/webapp/');
     $this->assertEquals($mock->MakeURL('/', TRUE), 'http://www.bluestatic.org/hoplite/webapp/');
 
     $globals['_SERVER']['HTTPS'] = 'on';
     $globals['_SERVER']['SERVER_PORT'] = 443;
-    $mock = new \hoplite\http\RootController($globals);
+    $mock = new \hoplite\http\FrontController($globals);
     $this->assertEquals($mock->MakeURL('/'), '/hoplite/webapp/');
     $this->assertEquals($mock->MakeURL('/', TRUE), 'https://www.bluestatic.org/hoplite/webapp/');
 
@@ -241,9 +233,60 @@ class RootControllerTest extends \PHPUnit_Framework_TestCase
     $this->assertEquals($mock->MakeURL('/path/3', TRUE), 'https://www.bluestatic.org/hoplite/webapp/path/3');
 
     $globals['_SERVER']['SERVER_PORT'] = 8080;
-    $mock = new \hoplite\http\RootController($globals);
+    $mock = new \hoplite\http\FrontController($globals);
     $this->assertEquals($mock->MakeURL('/path/2'), '/hoplite/webapp/path/2');
     $this->assertEquals($mock->MakeURL('/', TRUE), 'https://www.bluestatic.org:8080/hoplite/webapp/');
     $this->assertEquals($mock->MakeURL('/path/3', TRUE), 'https://www.bluestatic.org:8080/hoplite/webapp/path/3');
   }
+
+  public function testThreeInterceptors()
+  {
+    $interceptors = [
+      new ClosureInterceptor(NULL),
+      new ClosureInterceptor(NULL),
+      new ClosureInterceptor(NULL),
+    ];
+
+    $mock = new \hoplite\http\FrontController([]);
+    $mock->set_url_map(new http\UrlMap($mock));
+
+    foreach ($interceptors as $interceptor) {
+      $mock->AddInterceptor($interceptor);
+    }
+
+    $mock->RouteRequest($mock->request());
+
+    foreach ($interceptors as $interceptor) {
+      $this->assertTrue($interceptor->did_intercept);
+    }
+  }
+
+  public function testInterruptInterceptor()
+  {
+    $i1 = new ClosureInterceptor(NULL);
+    $i2 = new ClosureInterceptor(function($controller, $action, $request, $response) {
+      $controller->SendResponseCode(400);
+    });
+    $test = $this;
+    $i3 = new ClosureInterceptor(function($controller, $action, $request, $response) use ($test) {
+      $test->assertEquals(400, $response->response_code);
+    });
+
+    $mock = $this->ConfigureMock(['SendResponse'], []);
+    $mock->set_url_map(new http\UrlMap($mock));
+    $mock->AddInterceptor($i1);
+    $mock->AddInterceptor($i2);
+    $mock->AddInterceptor($i3);
+
+    $mock->expects($this->once())
+         ->method('SendResponse');
+
+    $mock->RouteRequest($mock->request());
+
+    $this->assertEquals(400, $mock->response()->response_code);
+    $this->assertTrue($i1->did_intercept);
+    $this->assertTrue($i2->did_intercept);
+    // i3 would not normally run but because _Exit is mocked, the script does
+    // not finish.
+  }
 }
index b101e2453705060690dc24d349f3dc2112f14a92..dba9e74f8ee2b2763d0ef334106789b84ecc982b 100644 (file)
@@ -31,7 +31,7 @@ class OutputFilterTest extends \PHPUnit_Framework_TestCase
 {
   public function setUp()
   {
-    $this->fixture = new TestOutputFilter(new http\RootController(array()));
+    $this->fixture = new TestOutputFilter(new http\FrontController(array()));
   }
 
   public function testEncodeXML()
@@ -50,7 +50,7 @@ class OutputFilterTest extends \PHPUnit_Framework_TestCase
 <response><test>1</test><foo>bar</foo><bar>&lt;strong&gt;baz&lt;/strong&gt;</bar><baz><poo>moo</poo><moo>baa</moo></baz></response>
 
 XML;
-    
+
     $this->assertEquals($expected, $this->fixture->T_EncodeXML($array));
 
     $obj = new \stdClass();
index fb6c36b1a4f4abdf5f87ee0ec265ea0c78857bea..afae0db51d44f356fc6b27ec7bc032a501e0ef9d 100644 (file)
@@ -1,11 +1,11 @@
 <?php
 // Hoplite
 // Copyright (c) 2011 Blue Static
-// 
+//
 // This program is free software: you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by the Free
 // Software Foundation, either version 3 of the License, or any later version.
-// 
+//
 // This program is distributed in the hope that it will be useful, but WITHOUT
 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
@@ -17,8 +17,8 @@
 namespace hoplite\test;
 use hoplite\http as http;
 
+require_once HOPLITE_ROOT . '/http/front_controller.php';
 require_once HOPLITE_ROOT . '/http/rest_action.php';
-require_once HOPLITE_ROOT . '/http/root_controller.php';
 require_once TEST_ROOT . '/tests/http/fixtures.php';
 
 class RestActionTest extends \PHPUnit_Framework_TestCase
@@ -26,7 +26,7 @@ class RestActionTest extends \PHPUnit_Framework_TestCase
   public function setUp()
   {
     $globals = array();
-    $this->fixture = new TestRestAction(new http\RootController($globals));
+    $this->fixture = new TestRestAction(new http\FrontController($globals));
     $this->request = new http\Request();
     $this->response = new http\Response();
   }
@@ -72,7 +72,7 @@ class RestActionTest extends \PHPUnit_Framework_TestCase
   public function testInvalid()
   {
     $globals = array();
-    $mock = $this->getMock('hoplite\http\RootController', array('Stop'), array($globals));
+    $mock = $this->getMock('hoplite\http\FrontController', array('Stop'), array($globals));
 
     $this->fixture = new TestRestAction($mock);
 
index 629d52a45f1e99d02c0038f14b60fc9dbed27128..7a4d01bc96360c54268f41c76232965247655e2f 100644 (file)
@@ -39,7 +39,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   public function setUp()
   {
     $globals = array();
-    $this->controller = new http\RootController($globals);
+    $this->controller = new http\FrontController($globals);
     $this->fixture = new TestRestAdapter($this->controller);
     $this->request = $this->controller->request();
     $this->response = $this->controller->response();
@@ -59,7 +59,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'GET';
     $this->request->data['action'] = 'fetch';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue('did_get');
   }
 
@@ -67,7 +67,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'POST';
     $this->request->data['action'] = 'fetch';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue('did_get');
   }
 
@@ -75,7 +75,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'PUT';
     $this->request->data['action'] = 'fetch';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue(NULL);
     $this->assertEquals(http\ResponseCode::METHOD_NOT_ALLOWED, $this->response->response_code);
   }
@@ -84,7 +84,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'POST';
     $this->request->data['action'] = 'update';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue('did_post');
   }
 
@@ -92,7 +92,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'GET';
     $this->request->data['action'] = 'update';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue(NULL);
     $this->assertEquals(http\ResponseCode::METHOD_NOT_ALLOWED, $this->response->response_code);
   }
@@ -101,7 +101,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'POST';
     $this->request->data['action'] = 'delete';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue('did_delete');
   }
 
@@ -109,7 +109,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'GET';
     $this->request->data['action'] = 'delete';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue(NULL);
     $this->assertEquals(http\ResponseCode::METHOD_NOT_ALLOWED, $this->response->response_code);
   }
@@ -118,7 +118,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'POST';
     $this->request->data['action'] = 'insert';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue('did_put');
   }
 
@@ -126,7 +126,7 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   {
     $this->request->http_method = 'GET';
     $this->request->data['action'] = 'insert';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($this->request, $this->response);
     $this->RestExpectSingleTrue(NULL);
     $this->assertEquals(http\ResponseCode::METHOD_NOT_ALLOWED, $this->response->response_code);
   }
@@ -134,17 +134,17 @@ class RestAdapterTest extends \PHPUnit_Framework_TestCase
   public function testInvalid()
   {
     $globals = array();
-    $mock = $this->getMock('hoplite\http\RootController', array('Stop'), array($globals));
+    $mock = $this->getMock('hoplite\http\FrontController', array('SendResponse'), array($globals));
 
     $this->fixture = new TestRestAdapter($mock);
 
     $mock->expects($this->once())
-         ->method('Stop');
+         ->method('SendResponse');
 
     $this->request->http_method = 'HEAD';
-    $this->controller->InvokeAction($this->fixture);
+    $this->fixture->Invoke($mock->request(), $mock->response());
     $this->RestExpectSingleTrue('___none___');
 
-    $this->assertEquals(http\ResponseCode::NOT_FOUND, $this->response->response_code);
+    $this->assertEquals(http\ResponseCode::NOT_FOUND, $mock->response()->response_code);
   }
 }
index 72fbaf28e12c62b6a2df3e31c8360fde7b136056..01fd813ca5b6aaffbbb46bf6a3e4e5905ff37b48 100644 (file)
@@ -29,7 +29,7 @@ class UrlMapTest extends \PHPUnit_Framework_TestCase
   public function setUp()
   {
     $globals = array();
-    $this->fixture = new http\UrlMap(new http\RootController($globals));
+    $this->fixture = new http\UrlMap(new http\FrontController($globals));
   }
 
   public function testSimpleEvaluate()