Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch '2.2-middleware' into 2.2

  • Loading branch information...
commit 8465538800f581b89a872f191b57a434860a36e0 2 parents b1e7164 + 15dcd60
@lorenzo lorenzo authored
View
22 app/Config/bootstrap.php
@@ -122,3 +122,25 @@
* CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit
*
*/
+
+
+/**
+ * You can attach event listeners to the request lifecyle as Dispatcher Filter . By Default CakePHP bundles two filters:
+ *
+ * - AssetDispatcher filter will serve your asset files (css, images, js, etc) from your themes and plugins
+ * - CacheDispatcher filter will read the Cache.check configure variable and try to serve cached content generated from controllers
+ *
+ * Feel free to remove or add filters as you see fit for your application. A few examples:
+ *
+ * Configure::write('Dispatcher.filters', array(
+ * 'MyCacheFilter', // will use MyCacheFilter class from the Routing/Filter package in your app.
+ * 'MyPlugin.MyFilter', // will use MyFilter class from the Routing/Filter package in MyPlugin plugin.
+ * array('callable' => $aFunction, 'on' => 'before', 'priority' => 9), // A valid PHP callback type to be called on beforeDispatch
+ * array('callable' => $anotherMethod, 'on' => 'after'), // A valid PHP callback type to be called on afterDispatch
+ *
+ * ));
+ */
+Configure::write('Dispatcher.filters', array(
+ 'AssetDispatcher',
+ 'CacheDispatcher'
+));
View
21 lib/Cake/Console/Templates/skel/Config/bootstrap.php
@@ -63,3 +63,24 @@
* CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit
*
*/
+
+/**
+ * You can attach event listeners to the request lifecyle as Dispatcher Filter . By Default CakePHP bundles two filters:
+ *
+ * - AssetDispatcher filter will serve your asset files (css, images, js, etc) from your themes and plugins
+ * - CacheDispatcher filter will read the Cache.check configure variable and try to serve cached content generated from controllers
+ *
+ * Feel free to remove or add filters as you see fit for your application. A few examples:
+ *
+ * Configure::write('Dispatcher.filters', array(
+ * 'MyCacheFilter', // will use MyCacheFilter class from the Routing/Filter package in your app.
+ * 'MyPlugin.MyFilter', // will use MyFilter class from the Routing/Filter package in MyPlugin plugin.
+ * array('callbale' => $aFunction, 'on' => 'before', 'priority' => 9), // A valid PHP callback type to be called on beforeDispatch
+ * array('callbale' => $anotherMethod, 'on' => 'after'), // A valid PHP callback type to be called on afterDispatch
+ *
+ * ));
+ */
+Configure::write('Dispatcher.filters', array(
+ 'AssetDispatcher',
+ 'CacheDispatcher'
+));
View
11 lib/Cake/Error/exceptions.php
@@ -439,6 +439,17 @@ class MissingPluginException extends CakeException {
}
/**
+ * Exception raised when a Dispatcher filter could not be found
+ *
+ * @package Cake.Error
+ */
+class MissingDispatcherFilterException extends CakeException {
+
+ protected $_messageTemplate = 'Dispatcher filter %s could not be found.';
+
+}
+
+/**
* Exception class for AclComponent and Interface implementations.
*
* @package Cake.Error
View
240 lib/Cake/Routing/Dispatcher.php
@@ -27,6 +27,9 @@
App::uses('Scaffold', 'Controller');
App::uses('View', 'View');
App::uses('Debugger', 'Utility');
+App::uses('CakeEvent', 'Event');
+App::uses('CakeEventManager', 'Event');
+App::uses('CakeEventListener', 'Event');
/**
* Dispatcher converts Requests into controller actions. It uses the dispatched Request
@@ -35,7 +38,14 @@
*
* @package Cake.Routing
*/
-class Dispatcher {
+class Dispatcher implements CakeEventListener {
+
+/**
+ * Event manager, used to handle dispatcher filters
+ *
+ * @var CakeEventMaanger
+ **/
+ protected $_eventManager;
/**
* Constructor.
@@ -49,6 +59,65 @@ public function __construct($base = false) {
}
/**
+ * Returns the CakeEventManager instance or creates one if none was
+ * creted. Attaches the default listeners and filters
+ *
+ * @return CakeEventmanger
+ **/
+ public function getEventManager() {
+ if (!$this->_eventManager) {
+ $this->_eventManager = new CakeEventManager();
+ $this->_eventManager->attach($this);
+ $this->_attachFilters($this->_eventManager);
+ }
+ return $this->_eventManager;
+ }
+
+/**
+ * Returns the list of events this object listents to.
+ *
+ * @return array
+ **/
+ public function implementedEvents() {
+ return array('Dispatcher.beforeDispatch' => 'parseParams');
+ }
+
+/**
+ * Attaches all event listeners for this dispatcher instance. Loads the
+ * dispatcher filters from the configured locations.
+ *
+ * @param CakeEventManager $manager
+ * @return void
+ **/
+ protected function _attachFilters($manager) {
+ $filters = Configure::read('Dispatcher.filters');
+ if (empty($filters)) {
+ return;
+ }
+
+ foreach ($filters as $filter) {
+ if (is_string($filter)) {
+ $filter = array('callable' => $filter);
+ }
+ if (is_string($filter['callable'])) {
+ list($plugin, $callable) = pluginSplit($filter['callable'], true);
+ App::uses($callable, $plugin . 'Routing/Filter');
+ if (!class_exists($callable)) {
+ throw new MissingDispatcherFilterException($callable);
+ }
+ $manager->attach(new $callable);
+ } else {
+ $on = strtolower($filter['on']);
+ $options = array();
+ if (isset($filter['priority'])) {
+ $options = array('priority' => $filter['priority']);
+ }
+ $manager->attach($filter['callable'], 'Dispatcher.' . $on . 'Dispatch', $options);
+ }
+ }
+ }
+
+/**
* Dispatches and invokes given Request, handing over control to the involved controller. If the controller is set
* to autoRender, via Controller::$autoRender, then Dispatcher will render the view.
*
@@ -63,16 +132,23 @@ public function __construct($base = false) {
* @param CakeRequest $request Request object to dispatch.
* @param CakeResponse $response Response object to put the results of the dispatch into.
* @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params
- * @return boolean Success
+ * @return string|void if `$request['return']` is set then it returns response body, null otherwise
* @throws MissingControllerException When the controller is missing.
*/
public function dispatch(CakeRequest $request, CakeResponse $response, $additionalParams = array()) {
- if ($this->asset($request->url, $response) || $this->cached($request->here())) {
+ $beforeEvent = new CakeEvent('Dispatcher.beforeDispatch', $this, compact('request', 'response', 'additionalParams'));
+ $this->getEventManager()->dispatch($beforeEvent);
+
+ $request = $beforeEvent->data['request'];
+ if ($beforeEvent->result instanceof CakeResponse) {
+ if (isset($request->params['return'])) {
+ return $response->body();
+ }
+ $response->send();
return;
}
Router::setRequestInfo($request);
- $request = $this->parseParams($request, $additionalParams);
$controller = $this->_getController($request, $response);
if (!($controller instanceof Controller)) {
@@ -82,7 +158,14 @@ public function dispatch(CakeRequest $request, CakeResponse $response, $addition
));
}
- return $this->_invoke($controller, $request, $response);
+ $response = $this->_invoke($controller, $request, $response);
+ if (isset($request->params['return'])) {
+ return $response->body();
+ }
+
+ $afterEvent = new CakeEvent('Dispatcher.afterDispatch', $this, compact('request', 'response'));
+ $this->getEventManager()->dispatch($afterEvent);
+ $afterEvent->data['response']->send();
}
/**
@@ -93,7 +176,7 @@ public function dispatch(CakeRequest $request, CakeResponse $response, $addition
* @param Controller $controller Controller to invoke
* @param CakeRequest $request The request object to invoke the controller for.
* @param CakeResponse $response The response object to receive the output
- * @return void
+ * @return CakeResponse te resulting response object
*/
protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) {
$controller->constructClasses();
@@ -113,22 +196,18 @@ protected function _invoke(Controller $controller, CakeRequest $request, CakeRes
}
$controller->shutdownProcess();
- if (isset($request->params['return'])) {
- return $response->body();
- }
- $response->send();
+ return $response;
}
/**
* Applies Routing and additionalParameters to the request to be dispatched.
* If Routes have not been loaded they will be loaded, and app/Config/routes.php will be run.
*
- * @param CakeRequest $request CakeRequest object to mine for parameter information.
- * @param array $additionalParams An array of additional parameters to set to the request.
- * Useful when Object::requestAction() is involved
- * @return CakeRequest The request object with routing params set.
+ * @param CakeEvent $event containing the request, response and additional params
+ * @return void
*/
- public function parseParams(CakeRequest $request, $additionalParams = array()) {
+ public function parseParams($event) {
+ $request = $event->data['request'];
if (count(Router::$routes) == 0) {
$namedExpressions = Router::getNamedExpressions();
extract($namedExpressions);
@@ -138,10 +217,9 @@ public function parseParams(CakeRequest $request, $additionalParams = array()) {
$params = Router::parse($request->url);
$request->addParams($params);
- if (!empty($additionalParams)) {
- $request->addParams($additionalParams);
+ if (!empty($event->data['additionalParams'])) {
+ $request->addParams($event->data['additionalParams']);
}
- return $request;
}
/**
@@ -199,130 +277,4 @@ protected function _loadRoutes() {
include APP . 'Config' . DS . 'routes.php';
}
-/**
- * Outputs cached dispatch view cache
- *
- * @param string $path Requested URL path with any query string parameters
- * @return string|boolean False if is not cached or output
- */
- public function cached($path) {
- if (Configure::read('Cache.check') === true) {
- if ($path == '/') {
- $path = 'home';
- }
- $path = strtolower(Inflector::slug($path));
-
- $filename = CACHE . 'views' . DS . $path . '.php';
-
- if (!file_exists($filename)) {
- $filename = CACHE . 'views' . DS . $path . '_index.php';
- }
- if (file_exists($filename)) {
- $controller = null;
- $view = new View($controller);
- return $view->renderCache($filename, microtime(true));
- }
- }
- return false;
- }
-
-/**
- * Checks if a requested asset exists and sends it to the browser
- *
- * @param string $url Requested URL
- * @param CakeResponse $response The response object to put the file contents in.
- * @return boolean True on success if the asset file was found and sent
- */
- public function asset($url, CakeResponse $response) {
- if (strpos($url, '..') !== false || strpos($url, '.') === false) {
- return false;
- }
- $filters = Configure::read('Asset.filter');
- $isCss = (
- strpos($url, 'ccss/') === 0 ||
- preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?<!css)/ccss)/#i', $url)
- );
- $isJs = (
- strpos($url, 'cjs/') === 0 ||
- preg_match('#^/((theme/[^/]+)/cjs/)|(([^/]+)(?<!js)/cjs)/#i', $url)
- );
- if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) {
- $response->statusCode(404);
- $response->send();
- return true;
- } elseif ($isCss) {
- include WWW_ROOT . DS . $filters['css'];
- return true;
- } elseif ($isJs) {
- include WWW_ROOT . DS . $filters['js'];
- return true;
- }
- $pathSegments = explode('.', $url);
- $ext = array_pop($pathSegments);
- $parts = explode('/', $url);
- $assetFile = null;
-
- if ($parts[0] === 'theme') {
- $themeName = $parts[1];
- unset($parts[0], $parts[1]);
- $fileFragment = urldecode(implode(DS, $parts));
- $path = App::themePath($themeName) . 'webroot' . DS;
- if (file_exists($path . $fileFragment)) {
- $assetFile = $path . $fileFragment;
- }
- } else {
- $plugin = Inflector::camelize($parts[0]);
- if (CakePlugin::loaded($plugin)) {
- unset($parts[0]);
- $fileFragment = urldecode(implode(DS, $parts));
- $pluginWebroot = CakePlugin::path($plugin) . 'webroot' . DS;
- if (file_exists($pluginWebroot . $fileFragment)) {
- $assetFile = $pluginWebroot . $fileFragment;
- }
- }
- }
-
- if ($assetFile !== null) {
- $this->_deliverAsset($response, $assetFile, $ext);
- return true;
- }
- return false;
- }
-
-/**
- * Sends an asset file to the client
- *
- * @param CakeResponse $response The response object to use.
- * @param string $assetFile Path to the asset file in the file system
- * @param string $ext The extension of the file to determine its mime type
- * @return void
- */
- protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
- ob_start();
- $compressionEnabled = Configure::read('Asset.compress') && $response->compress();
- if ($response->type($ext) == $ext) {
- $contentType = 'application/octet-stream';
- $agent = env('HTTP_USER_AGENT');
- if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
- $contentType = 'application/octetstream';
- }
- $response->type($contentType);
- }
- if (!$compressionEnabled) {
- $response->header('Content-Length', filesize($assetFile));
- }
- $response->cache(filemtime($assetFile));
- $response->send();
- ob_clean();
- if ($ext === 'css' || $ext === 'js') {
- include $assetFile;
- } else {
- readfile($assetFile);
- }
-
- if ($compressionEnabled) {
- ob_end_flush();
- }
- }
-
}
View
85 lib/Cake/Routing/DispatcherFilter.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Routing
+ * @since CakePHP(tm) v 2.2
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('CakeEventListener', 'Event');
+
+/**
+ * This abstract class represents a filter to be applied to a dispatcher cycle. It acts as as
+ * event listener with the ability to alter the request or response as needed before it is handled
+ * by a controller or after the response body has already been built.
+ *
+ * @package Cake.Routing
+ */
+abstract class DispatcherFilter implements CakeEventListener {
+
+/**
+ * Default priority for all methods in this filter
+ *
+ * @var int
+ **/
+ public $priority = 10;
+
+/**
+ * Returns the list of events this filter listens to.
+ * Dispatcher notifies 2 different events `Dispatcher.before` and `Dispatcher.after`.
+ * By default this class will attach `preDispatch` and `postDispatch` method respectively.
+ *
+ * Override this method at will to only listen to the events you are interested in.
+ *
+ * @return array
+ **/
+ public function implementedEvents() {
+ return array(
+ 'Dispatcher.beforeDispatch' => array('callable' => 'beforeDispatch', 'priority' => $this->priority),
+ 'Dispatcher.afterDispatch' => array('callable' => 'afterDispatch', 'priority' => $this->priority),
+ );
+ }
+
+/**
+ * Method called before the controller is instantiated and called to ser a request.
+ * If used with default priority, it will be called after the Router has parsed the
+ * url and set the routing params into the request object.
+ *
+ * If a CakeResponse object instance is returned, it will be served at the end of the
+ * event cycle, not calling any controller as a result. This will also have the effect of
+ * not calling the after event in the dispatcher.
+ *
+ * If false is returned, the event will be stopped and no more listeners will be notified.
+ * Alternatively you can call `$event->stopPropagation()` to acheive the same result.
+ *
+ * @param CakeEvent $event container object having the `request`, `response` and `additionalParams`
+ * keys in the data property.
+ * @return CakeResponse|boolean
+ **/
+ public function beforeDispatch($event) {
+ }
+
+/**
+ * Method called after the controller served a request and generated a response.
+ * It is posible to alter the response object at this point as it is not sent to the
+ * client yet.
+ *
+ * If false is returned, the event will be stopped and no more listeners will be notified.
+ * Alternatively you can call `$event->stopPropagation()` to acheive the same result.
+ *
+ * @param CakeEvent $event container object having the `request` and `response`
+ * keys in the data property.
+ * @return mixed boolean to stop the event dispatching or null to continue
+ **/
+ public function afterDispatch($event) {}
@ADmad Collaborator
ADmad added a note

Closing brace should be on new line, as per coding standard

@lorenzo Owner
lorenzo added a note

Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+}
View
159 lib/Cake/Routing/Filter/AssetDispatcher.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Routing
+ * @since CakePHP(tm) v 2.2
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('DispatcherFilter', 'Routing');
+
+/**
+ * Filters a request and tests whether it is a file in the webroot folder or not and
+ * serves the file to the client if appropriate.
+ *
+ * @package Cake.Routing.Filter
+ */
+class AssetDispatcher extends DispatcherFilter {
+
+/**
+ * Default priority for all methods in this filter
+ * This filter should run before the request gets parsed by router
+ *
+ * @var int
+ **/
+ public $priority = 9;
+
+/**
+ * Checks if a requested asset exists and sends it to the browser
+ *
+ * @param CakeEvent $event containing the request and response object
+ * @return CakeResponse if the client is requesting a recognized asset, null otherwise
+ */
+ public function beforeDispatch($event) {
+ $url = $event->data['request']->url;
+ $response = $event->data['response'];
+
+ if (strpos($url, '..') !== false || strpos($url, '.') === false) {
+ return;
+ }
+
+ if ($result = $this->_filterAsset($event)) {
+ $event->stopPropagation();
+ return $result;
+ }
+
+ $pathSegments = explode('.', $url);
+ $ext = array_pop($pathSegments);
+ $parts = explode('/', $url);
+ $assetFile = null;
+
+ if ($parts[0] === 'theme') {
+ $themeName = $parts[1];
+ unset($parts[0], $parts[1]);
+ $fileFragment = urldecode(implode(DS, $parts));
+ $path = App::themePath($themeName) . 'webroot' . DS;
+ if (file_exists($path . $fileFragment)) {
+ $assetFile = $path . $fileFragment;
+ }
+ } else {
+ $plugin = Inflector::camelize($parts[0]);
+ if (CakePlugin::loaded($plugin)) {
+ unset($parts[0]);
+ $fileFragment = urldecode(implode(DS, $parts));
+ $pluginWebroot = CakePlugin::path($plugin) . 'webroot' . DS;
+ if (file_exists($pluginWebroot . $fileFragment)) {
+ $assetFile = $pluginWebroot . $fileFragment;
+ }
+ }
+ }
+
+ if ($assetFile !== null) {
+ $event->stopPropagation();
+ $response->modified(filemtime($assetFile));
+ if (!$response->checkNotModified($event->data['request'])) {
+ $this->_deliverAsset($response, $assetFile, $ext);
+ }
+ return $response;
+ }
+ }
+
+/**
+ * Checks if the client is requeting a filtered asset and runs the corresponding
+ * filter if any is configured
+ *
+ * @param CakeEvent $event containing the request and response object
+ * @return CakeResponse if the client is requesting a recognized asset, null otherwise
+ */
+ protected function _filterAsset($event) {
+ $url = $event->data['request']->url;
+ $response = $event->data['response'];
+ $filters = Configure::read('Asset.filter');
+ $isCss = (
+ strpos($url, 'ccss/') === 0 ||
+ preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?<!css)/ccss)/#i', $url)
+ );
+ $isJs = (
+ strpos($url, 'cjs/') === 0 ||
+ preg_match('#^/((theme/[^/]+)/cjs/)|(([^/]+)(?<!js)/cjs)/#i', $url)
+ );
+
+ if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) {
+ $response->statusCode(404);
+ return $response;
+ } elseif ($isCss) {
+ include WWW_ROOT . DS . $filters['css'];
+ return $response;
+ } elseif ($isJs) {
+ include WWW_ROOT . DS . $filters['js'];
+ return $response;
+ }
+ }
+
+/**
+ * Sends an asset file to the client
+ *
+ * @param CakeResponse $response The response object to use.
+ * @param string $assetFile Path to the asset file in the file system
+ * @param string $ext The extension of the file to determine its mime type
+ * @return void
+ */
+ protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
+ ob_start();
+ $compressionEnabled = Configure::read('Asset.compress') && $response->compress();
+ if ($response->type($ext) == $ext) {
+ $contentType = 'application/octet-stream';
+ $agent = env('HTTP_USER_AGENT');
+ if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
+ $contentType = 'application/octetstream';
+ }
+ $response->type($contentType);
+ }
+ if (!$compressionEnabled) {
+ $response->header('Content-Length', filesize($assetFile));
+ }
+ $response->cache(filemtime($assetFile));
+ $response->send();
+ ob_clean();
+ if ($ext === 'css' || $ext === 'js') {
+ include $assetFile;
+ } else {
+ readfile($assetFile);
+ }
+
+ if ($compressionEnabled) {
+ ob_end_flush();
+ }
+ }
+
+}
View
71 lib/Cake/Routing/Filter/CacheDispatcher.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Routing
+ * @since CakePHP(tm) v 2.2
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('DispatcherFilter', 'Routing');
+
+/**
+ * This filter will check wheter the response was previously cached in the file system
+ * and served it back to the client if appropriate.
+ *
+ * @package Cake.Routing.Filter
+ */
+class CacheDispatcher extends DispatcherFilter {
+
+/**
+ * Default priority for all methods in this filter
+ * This filter should run before the request gets parsed by router
+ *
+ * @var int
+ **/
+ public $priority = 9;
+
+/**
+ * Checks whether the response was cached and set the body accordingly.
+ *
+ * @param CakeEvent $event containing the request and response object
+ * @return CakeResponse with cached content if found, null otherwise
+ */
+ public function beforeDispatch($event) {
+ if (Configure::read('Cache.check') !== true) {
+ return;
+ }
+
+ $path = $event->data['request']->here();
+ if ($path == '/') {
+ $path = 'home';
+ }
+ $path = strtolower(Inflector::slug($path));
+
+ $filename = CACHE . 'views' . DS . $path . '.php';
+
+ if (!file_exists($filename)) {
+ $filename = CACHE . 'views' . DS . $path . '_index.php';
+ }
+ if (file_exists($filename)) {
+ $controller = null;
+ $view = new View($controller);
+ $result = $view->renderCache($filename, microtime(true));
+ if ($result !== false) {
+ $event->stopPropagation();
+ $event->data['response']->body($result);
+ return $event->data['response'];
+ }
+ }
+ }
+
+}
View
1  lib/Cake/Test/Case/AllRoutingTest.php
@@ -38,6 +38,7 @@ public static function suite() {
$suite->addTestDirectory($libs . 'Routing');
$suite->addTestDirectory($libs . 'Routing' . DS . 'Route');
+ $suite->addTestDirectory($libs . 'Routing' . DS . 'Filter');
return $suite;
}
}
View
504 lib/Cake/Test/Case/Routing/DispatcherTest.php
@@ -45,6 +45,13 @@ protected function _sendHeader($name, $value = null) {
class TestDispatcher extends Dispatcher {
/**
+ * Controller instance, made publicly available for testing
+ *
+ * @var Controller
+ **/
+ public $controller;
+
+/**
* invoke method
*
* @param mixed $controller
@@ -52,8 +59,29 @@ class TestDispatcher extends Dispatcher {
* @return void
*/
protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) {
- $result = parent::_invoke($controller, $request, $response);
- return $controller;
+ $this->controller = $controller;
+ return parent::_invoke($controller, $request, $response);
+ }
+
+/**
+ * Helper function to test single method attaching for dispatcher filters
+ *
+ * @param CakeEvent
+ * @return void
+ **/
+ public function filterTest($event) {
+ $event->data['request']->params['eventName'] = $event->name();
+ }
+
+/**
+ * Helper function to test single method attaching for dispatcher filters
+ *
+ * @param CakeEvent
+ * @return void
+ **/
+ public function filterTest2($event) {
+ $event->stopPropagation();
+ return $event->data['response'];
}
}
@@ -556,6 +584,7 @@ public function tearDown() {
Configure::write('App', $this->_app);
Configure::write('Cache', $this->_cache);
Configure::write('debug', $this->_debug);
+ Configure::write('Dispatcher.filters', array());
}
/**
@@ -565,14 +594,15 @@ public function tearDown() {
*/
public function testParseParamsWithoutZerosAndEmptyPost() {
$Dispatcher = new Dispatcher();
-
- $test = $Dispatcher->parseParams(new CakeRequest("/testcontroller/testaction/params1/params2/params3"));
- $this->assertSame($test['controller'], 'testcontroller');
- $this->assertSame($test['action'], 'testaction');
- $this->assertSame($test['pass'][0], 'params1');
- $this->assertSame($test['pass'][1], 'params2');
- $this->assertSame($test['pass'][2], 'params3');
- $this->assertFalse(!empty($test['form']));
+ $request = new CakeRequest("/testcontroller/testaction/params1/params2/params3");
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $request));
+ $Dispatcher->parseParams($event);
+ $this->assertSame($request['controller'], 'testcontroller');
+ $this->assertSame($request['action'], 'testaction');
+ $this->assertSame($request['pass'][0], 'params1');
+ $this->assertSame($request['pass'][1], 'params2');
+ $this->assertSame($request['pass'][2], 'params3');
+ $this->assertFalse(!empty($request['form']));
}
/**
@@ -583,9 +613,11 @@ public function testParseParamsWithoutZerosAndEmptyPost() {
public function testParseParamsReturnsPostedData() {
$_POST['testdata'] = "My Posted Content";
$Dispatcher = new Dispatcher();
-
- $test = $Dispatcher->parseParams(new CakeRequest("/"));
- $this->assertEquals("My Posted Content", $test['data']['testdata']);
+ $request = new CakeRequest("/");
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $request));
+ $Dispatcher->parseParams($event);
+ $test = $Dispatcher->parseParams($event);
+ $this->assertEquals("My Posted Content", $request['data']['testdata']);
}
/**
@@ -595,7 +627,10 @@ public function testParseParamsReturnsPostedData() {
*/
public function testParseParamsWithSingleZero() {
$Dispatcher = new Dispatcher();
- $test = $Dispatcher->parseParams(new CakeRequest("/testcontroller/testaction/1/0/23"));
+ $test = new CakeRequest("/testcontroller/testaction/1/0/23");
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $test));
+ $Dispatcher->parseParams($event);
+
$this->assertSame($test['controller'], 'testcontroller');
$this->assertSame($test['action'], 'testaction');
$this->assertSame($test['pass'][0], '1');
@@ -610,7 +645,10 @@ public function testParseParamsWithSingleZero() {
*/
public function testParseParamsWithManySingleZeros() {
$Dispatcher = new Dispatcher();
- $test = $Dispatcher->parseParams(new CakeRequest("/testcontroller/testaction/0/0/0/0/0/0"));
+ $test = new CakeRequest("/testcontroller/testaction/0/0/0/0/0/0");
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $test));
+ $Dispatcher->parseParams($event);
+
$this->assertRegExp('/\\A(?:0)\\z/', $test['pass'][0]);
$this->assertRegExp('/\\A(?:0)\\z/', $test['pass'][1]);
$this->assertRegExp('/\\A(?:0)\\z/', $test['pass'][2]);
@@ -626,8 +664,10 @@ public function testParseParamsWithManySingleZeros() {
*/
public function testParseParamsWithManyZerosInEachSectionOfUrl() {
$Dispatcher = new Dispatcher();
- $request = new CakeRequest("/testcontroller/testaction/000/0000/00000/000000/000000/0000000");
- $test = $Dispatcher->parseParams($request);
+ $test = new CakeRequest("/testcontroller/testaction/000/0000/00000/000000/000000/0000000");
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $test));
+ $Dispatcher->parseParams($event);
+
$this->assertRegExp('/\\A(?:000)\\z/', $test['pass'][0]);
$this->assertRegExp('/\\A(?:0000)\\z/', $test['pass'][1]);
$this->assertRegExp('/\\A(?:00000)\\z/', $test['pass'][2]);
@@ -643,9 +683,10 @@ public function testParseParamsWithManyZerosInEachSectionOfUrl() {
*/
public function testParseParamsWithMixedOneToManyZerosInEachSectionOfUrl() {
$Dispatcher = new Dispatcher();
+ $test = new CakeRequest("/testcontroller/testaction/01/0403/04010/000002/000030/0000400");
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $test));
+ $Dispatcher->parseParams($event);
- $request = new CakeRequest("/testcontroller/testaction/01/0403/04010/000002/000030/0000400");
- $test = $Dispatcher->parseParams($request);
$this->assertRegExp('/\\A(?:01)\\z/', $test['pass'][0]);
$this->assertRegExp('/\\A(?:0403)\\z/', $test['pass'][1]);
$this->assertRegExp('/\\A(?:04010)\\z/', $test['pass'][2]);
@@ -667,22 +708,25 @@ public function testQueryStringOnRoot() {
$_GET = array('coffee' => 'life', 'sleep' => 'sissies');
$Dispatcher = new Dispatcher();
- $uri = new CakeRequest('posts/home/?coffee=life&sleep=sissies');
- $result = $Dispatcher->parseParams($uri);
- $this->assertRegExp('/posts/', $result['controller']);
- $this->assertRegExp('/home/', $result['action']);
- $this->assertTrue(isset($result['url']['sleep']));
- $this->assertTrue(isset($result['url']['coffee']));
+ $request = new CakeRequest('posts/home/?coffee=life&sleep=sissies');
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $request));
+ $Dispatcher->parseParams($event);
+
+ $this->assertRegExp('/posts/', $request['controller']);
+ $this->assertRegExp('/home/', $request['action']);
+ $this->assertTrue(isset($request['url']['sleep']));
+ $this->assertTrue(isset($request['url']['coffee']));
$Dispatcher = new Dispatcher();
- $uri = new CakeRequest('/?coffee=life&sleep=sissy');
-
- $result = $Dispatcher->parseParams($uri);
- $this->assertRegExp('/pages/', $result['controller']);
- $this->assertRegExp('/display/', $result['action']);
- $this->assertTrue(isset($result['url']['sleep']));
- $this->assertTrue(isset($result['url']['coffee']));
- $this->assertEquals('life', $result['url']['coffee']);
+ $request = new CakeRequest('/?coffee=life&sleep=sissy');
+
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $request));
+ $Dispatcher->parseParams($event);
+ $this->assertRegExp('/pages/', $request['controller']);
+ $this->assertRegExp('/display/', $request['action']);
+ $this->assertTrue(isset($request['url']['sleep']));
+ $this->assertTrue(isset($request['url']['coffee']));
+ $this->assertEquals('life', $request['url']['coffee']);
}
/**
@@ -700,7 +744,7 @@ public function testMissingController() {
$url = new CakeRequest('some_controller/home/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
}
/**
@@ -718,7 +762,7 @@ public function testMissingControllerInterface() {
$url = new CakeRequest('dispatcher_test_interface/index');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
}
/**
@@ -736,9 +780,10 @@ public function testMissingControllerAbstract() {
$url = new CakeRequest('dispatcher_test_abstract/index');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
}
+
/**
* testDispatch method
*
@@ -753,27 +798,27 @@ public function testDispatchBasic() {
$url = new CakeRequest('pages/home/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
$expected = 'Pages';
- $this->assertEquals($expected, $controller->name);
+ $this->assertEquals($expected, $Dispatcher->controller->name);
$expected = array('0' => 'home', 'param' => 'value', 'param2' => 'value2');
- $this->assertSame($expected, $controller->passedArgs);
+ $this->assertSame($expected, $Dispatcher->controller->passedArgs);
- Configure::write('App.baseUrl','/pages/index.php');
+ Configure::write('App.baseUrl', '/pages/index.php');
$url = new CakeRequest('pages/home');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
$expected = 'Pages';
- $this->assertEquals($expected, $controller->name);
+ $this->assertEquals($expected, $Dispatcher->controller->name);
$url = new CakeRequest('pages/home/');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertNull($controller->plugin);
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertNull($Dispatcher->controller->plugin);
$expected = 'Pages';
- $this->assertEquals($expected, $controller->name);
+ $this->assertEquals($expected, $Dispatcher->controller->name);
unset($Dispatcher);
@@ -782,24 +827,24 @@ public function testDispatchBasic() {
Configure::write('App.baseUrl', '/timesheets/index.php');
$url = new CakeRequest('timesheets');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
$expected = 'Timesheets';
- $this->assertEquals($expected, $controller->name);
+ $this->assertEquals($expected, $Dispatcher->controller->name);
$url = new CakeRequest('timesheets/');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('Timesheets', $controller->name);
+ $this->assertEquals('Timesheets', $Dispatcher->controller->name);
$this->assertEquals('/timesheets/index.php', $url->base);
$url = new CakeRequest('test_dispatch_pages/camelCased');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('TestDispatchPages', $controller->name);
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertEquals('TestDispatchPages', $Dispatcher->controller->name);
$url = new CakeRequest('test_dispatch_pages/camelCased/something. .');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('something. .', $controller->params['pass'][0], 'Period was chopped off. %s');
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertEquals('something. .', $Dispatcher->controller->params['pass'][0], 'Period was chopped off. %s');
}
/**
@@ -834,18 +879,18 @@ public function testAdminDispatch() {
$response = $this->getMock('CakeResponse');
Router::reload();
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('TestDispatchPages', $controller->name);
+ $this->assertEquals('TestDispatchPages', $Dispatcher->controller->name);
- $this->assertSame($controller->passedArgs, array('param' => 'value', 'param2' => 'value2'));
- $this->assertTrue($controller->params['admin']);
+ $this->assertSame($Dispatcher->controller->passedArgs, array('param' => 'value', 'param2' => 'value2'));
+ $this->assertTrue($Dispatcher->controller->params['admin']);
$expected = '/cake/repo/branches/1.2.x.x/index.php/admin/test_dispatch_pages/index/param:value/param2:value2';
- $this->assertSame($expected, $controller->here);
+ $this->assertSame($expected, $Dispatcher->controller->here);
$expected = '/cake/repo/branches/1.2.x.x/index.php';
- $this->assertSame($expected, $controller->base);
+ $this->assertSame($expected, $Dispatcher->controller->base);
}
/**
@@ -865,22 +910,23 @@ public function testPluginDispatch() {
$url = new CakeRequest('my_plugin/some_pages/home/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
- $result = $Dispatcher->parseParams($url);
+ $event = new CakeEvent('DispatcherTest', $Dispatcher, array('request' => $url));
+ $Dispatcher->parseParams($event);
$expected = array(
'pass' => array('home'),
'named' => array('param' => 'value', 'param2' => 'value2'), 'plugin' => 'my_plugin',
'controller' => 'some_pages', 'action' => 'display'
);
foreach ($expected as $key => $value) {
- $this->assertEquals($value, $result[$key], 'Value mismatch ' . $key . ' %');
+ $this->assertEquals($value, $url[$key], 'Value mismatch ' . $key . ' %');
}
- $this->assertSame($controller->plugin, 'MyPlugin');
- $this->assertSame($controller->name, 'SomePages');
- $this->assertSame($controller->params['controller'], 'some_pages');
- $this->assertSame($controller->passedArgs, array('0' => 'home', 'param' => 'value', 'param2' => 'value2'));
+ $this->assertSame($Dispatcher->controller->plugin, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->name, 'SomePages');
+ $this->assertSame($Dispatcher->controller->params['controller'], 'some_pages');
+ $this->assertSame($Dispatcher->controller->passedArgs, array('0' => 'home', 'param' => 'value', 'param2' => 'value2'));
}
/**
@@ -903,12 +949,12 @@ public function testAutomaticPluginDispatch() {
$url = new CakeRequest('my_plugin/other_pages/index/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertSame($controller->plugin, 'MyPlugin');
- $this->assertSame($controller->name, 'OtherPages');
- $this->assertSame($controller->action, 'index');
- $this->assertSame($controller->passedArgs, array('param' => 'value', 'param2' => 'value2'));
+ $this->assertSame($Dispatcher->controller->plugin, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->name, 'OtherPages');
+ $this->assertSame($Dispatcher->controller->action, 'index');
+ $this->assertSame($Dispatcher->controller->passedArgs, array('param' => 'value', 'param2' => 'value2'));
$expected = '/cake/repo/branches/1.2.x.x/my_plugin/other_pages/index/param:value/param2:value2';
$this->assertSame($expected, $url->here);
@@ -936,12 +982,12 @@ public function testAutomaticPluginControllerDispatch() {
$url = new CakeRequest('my_plugin/my_plugin/add/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertSame($controller->plugin, 'MyPlugin');
- $this->assertSame($controller->name, 'MyPlugin');
- $this->assertSame($controller->action, 'add');
- $this->assertEquals(array('param' => 'value', 'param2' => 'value2'), $controller->params['named']);
+ $this->assertSame($Dispatcher->controller->plugin, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->name, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->action, 'add');
+ $this->assertEquals(array('param' => 'value', 'param2' => 'value2'), $Dispatcher->controller->params['named']);
Router::reload();
require CAKE . 'Config' . DS . 'routes.php';
@@ -955,13 +1001,13 @@ public function testAutomaticPluginControllerDispatch() {
$pluginUrl = Inflector::underscore($plugin);
$url = new CakeRequest($pluginUrl);
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertSame($controller->plugin, 'MyPlugin');
- $this->assertSame($controller->name, 'MyPlugin');
- $this->assertSame($controller->action, 'index');
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertSame($Dispatcher->controller->plugin, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->name, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->action, 'index');
$expected = $pluginUrl;
- $this->assertEquals($expected, $controller->params['controller']);
+ $this->assertEquals($expected, $Dispatcher->controller->params['controller']);
Configure::write('Routing.prefixes', array('admin'));
@@ -972,19 +1018,19 @@ public function testAutomaticPluginControllerDispatch() {
$url = new CakeRequest('admin/my_plugin/my_plugin/add/5/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('my_plugin', $controller->params['plugin']);
- $this->assertEquals('my_plugin', $controller->params['controller']);
- $this->assertEquals('admin_add', $controller->params['action']);
- $this->assertEquals(array(5), $controller->params['pass']);
- $this->assertEquals(array('param' => 'value', 'param2' => 'value2'), $controller->params['named']);
- $this->assertSame($controller->plugin, 'MyPlugin');
- $this->assertSame($controller->name, 'MyPlugin');
- $this->assertSame($controller->action, 'admin_add');
+ $this->assertEquals('my_plugin', $Dispatcher->controller->params['plugin']);
+ $this->assertEquals('my_plugin', $Dispatcher->controller->params['controller']);
+ $this->assertEquals('admin_add', $Dispatcher->controller->params['action']);
+ $this->assertEquals(array(5), $Dispatcher->controller->params['pass']);
+ $this->assertEquals(array('param' => 'value', 'param2' => 'value2'), $Dispatcher->controller->params['named']);
+ $this->assertSame($Dispatcher->controller->plugin, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->name, 'MyPlugin');
+ $this->assertSame($Dispatcher->controller->action, 'admin_add');
$expected = array(0 => 5, 'param' => 'value', 'param2' => 'value2');
- $this->assertEquals($expected, $controller->passedArgs);
+ $this->assertEquals($expected, $Dispatcher->controller->passedArgs);
Configure::write('Routing.prefixes', array('admin'));
CakePlugin::load('ArticlesTest', array('path' => '/fake/path'));
@@ -993,10 +1039,10 @@ public function testAutomaticPluginControllerDispatch() {
$Dispatcher = new TestDispatcher();
- $controller = $Dispatcher->dispatch(new CakeRequest('admin/articles_test'), $response, array('return' => 1));
- $this->assertSame($controller->plugin, 'ArticlesTest');
- $this->assertSame($controller->name, 'ArticlesTest');
- $this->assertSame($controller->action, 'admin_index');
+ $Dispatcher->dispatch(new CakeRequest('admin/articles_test'), $response, array('return' => 1));
+ $this->assertSame($Dispatcher->controller->plugin, 'ArticlesTest');
+ $this->assertSame($Dispatcher->controller->name, 'ArticlesTest');
+ $this->assertSame($Dispatcher->controller->action, 'admin_index');
$expected = array(
'pass' => array(),
@@ -1009,7 +1055,7 @@ public function testAutomaticPluginControllerDispatch() {
'return' => 1
);
foreach ($expected as $key => $value) {
- $this->assertEquals($expected[$key], $controller->request[$key], 'Value mismatch ' . $key);
+ $this->assertEquals($expected[$key], $Dispatcher->controller->request[$key], 'Value mismatch ' . $key);
}
}
@@ -1029,11 +1075,11 @@ public function testAutomaticPluginDispatchWithShortAccess() {
$url = new CakeRequest('my_plugin/');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('my_plugin', $controller->params['controller']);
- $this->assertEquals('my_plugin', $controller->params['plugin']);
- $this->assertEquals('index', $controller->params['action']);
- $this->assertFalse(isset($controller->params['pass'][0]));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertEquals('my_plugin', $Dispatcher->controller->params['controller']);
+ $this->assertEquals('my_plugin', $Dispatcher->controller->params['plugin']);
+ $this->assertEquals('index', $Dispatcher->controller->params['action']);
+ $this->assertFalse(isset($Dispatcher->controller->params['pass'][0]));
}
/**
@@ -1058,25 +1104,25 @@ public function testPluginShortCutUrlsWithControllerThatNeedsToBeLoaded() {
$url = new CakeRequest('test_plugin/');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('test_plugin', $controller->params['controller']);
- $this->assertEquals('test_plugin', $controller->params['plugin']);
- $this->assertEquals('index', $controller->params['action']);
- $this->assertFalse(isset($controller->params['pass'][0]));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertEquals('test_plugin', $Dispatcher->controller->params['controller']);
+ $this->assertEquals('test_plugin', $Dispatcher->controller->params['plugin']);
+ $this->assertEquals('index', $Dispatcher->controller->params['action']);
+ $this->assertFalse(isset($Dispatcher->controller->params['pass'][0]));
$url = new CakeRequest('/test_plugin/tests/index');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('tests', $controller->params['controller']);
- $this->assertEquals('test_plugin', $controller->params['plugin']);
- $this->assertEquals('index', $controller->params['action']);
- $this->assertFalse(isset($controller->params['pass'][0]));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertEquals('tests', $Dispatcher->controller->params['controller']);
+ $this->assertEquals('test_plugin', $Dispatcher->controller->params['plugin']);
+ $this->assertEquals('index', $Dispatcher->controller->params['action']);
+ $this->assertFalse(isset($Dispatcher->controller->params['pass'][0]));
$url = new CakeRequest('/test_plugin/tests/index/some_param');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
- $this->assertEquals('tests', $controller->params['controller']);
- $this->assertEquals('test_plugin', $controller->params['plugin']);
- $this->assertEquals('index', $controller->params['action']);
- $this->assertEquals('some_param', $controller->params['pass'][0]);
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $this->assertEquals('tests', $Dispatcher->controller->params['controller']);
+ $this->assertEquals('test_plugin', $Dispatcher->controller->params['plugin']);
+ $this->assertEquals('index', $Dispatcher->controller->params['action']);
+ $this->assertEquals('some_param', $Dispatcher->controller->params['pass'][0]);
App::build();
}
@@ -1095,7 +1141,7 @@ public function testAutomaticPluginControllerMissingActionDispatch() {
$url = new CakeRequest('my_plugin/not_here/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
}
/**
@@ -1113,7 +1159,7 @@ public function testAutomaticPluginControllerIndexMissingAction() {
$url = new CakeRequest('my_plugin/param:value/param2:value2');
$response = $this->getMock('CakeResponse');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
}
/**
@@ -1132,19 +1178,142 @@ public function testTestPluginDispatch() {
$url = new CakeRequest('/test_plugin/tests/index');
$response = $this->getMock('CakeResponse');
- $result = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
$this->assertTrue(class_exists('TestsController'));
$this->assertTrue(class_exists('TestPluginAppController'));
$this->assertTrue(class_exists('PluginsComponent'));
- $this->assertEquals('tests', $result->params['controller']);
- $this->assertEquals('test_plugin', $result->params['plugin']);
- $this->assertEquals('index', $result->params['action']);
+ $this->assertEquals('tests', $Dispatcher->controller->params['controller']);
+ $this->assertEquals('test_plugin', $Dispatcher->controller->params['plugin']);
+ $this->assertEquals('index', $Dispatcher->controller->params['action']);
App::build();
}
/**
+ * Tests that it is possible to attach filter classes to the dispatch cycle
+ *
+ * @return void
+ **/
+ public function testDispatcherFilterSuscriber() {
+ App::build(array(
+ 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS),
+ 'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS)
+ ), App::RESET);
+
+ CakePlugin::load('TestPlugin');
+ Configure::write('Dispatcher.filters', array(
+ array('callable' => 'TestPlugin.TestDispatcherFilter')
+ ));
+ $dispatcher = new TestDispatcher();
+ $request = new CakeRequest('/');
+ $request->params['altered'] = false;
+ $response = $this->getMock('CakeResponse', array('send'));
+
+ $dispatcher->dispatch($request, $response);
+ $this->assertTrue($request->params['altered']);
+ $this->assertEquals(304, $response->statusCode());
+
+ Configure::write('Dispatcher.filters', array(
+ 'TestPlugin.Test2DispatcherFilter',
+ 'TestPlugin.TestDispatcherFilter'
+ ));
+ $dispatcher = new TestDispatcher();
+ $request = new CakeRequest('/');
+ $request->params['altered'] = false;
+ $response = $this->getMock('CakeResponse', array('send'));
+
+ $dispatcher->dispatch($request, $response);
+ $this->assertFalse($request->params['altered']);
+ $this->assertEquals(500, $response->statusCode());
+ $this->assertNull($dispatcher->controller);
+ }
+
+/**
+ * Tests that attaching an inexistent class as filter will throw an exception
+ *
+ * @expectedException MissingDispatcherFilterException
+ * @return void
+ **/
+ public function testDispatcherFilterSuscriberMissing() {
+ App::build(array(
+ 'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS)
+ ), App::RESET);
+
+ CakePlugin::load('TestPlugin');
+ Configure::write('Dispatcher.filters', array(
+ array('callable' => 'TestPlugin.NotAFilter')
+ ));
+ $dispatcher = new TestDispatcher();
+ $request = new CakeRequest('/');
+ $response = $this->getMock('CakeResponse', array('send'));
+ $dispatcher->dispatch($request, $response);
+ }
+
+
+/**
+ * Tests it is possible to attach single callables as filters
+ *
+ * @return void
+ **/
+ public function testDispatcherFilterCallable() {
+ App::build(array(
+ 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS)
+ ), App::RESET);
+
+ $dispatcher = new TestDispatcher();
+ Configure::write('Dispatcher.filters', array(
+ array('callable' => array($dispatcher, 'filterTest'), 'on' => 'before')
+ ));
+
+ $request = new CakeRequest('/');
+ $response = $this->getMock('CakeResponse', array('send'));
+ $dispatcher->dispatch($request, $response);
+ $this->assertEquals('Dispatcher.beforeDispatch', $request->params['eventName']);
+
+ $dispatcher = new TestDispatcher();
+ Configure::write('Dispatcher.filters', array(
+ array('callable' => array($dispatcher, 'filterTest'), 'on' => 'after')
+ ));
+
+ $request = new CakeRequest('/');
+ $response = $this->getMock('CakeResponse', array('send'));
+ $dispatcher->dispatch($request, $response);
+ $this->assertEquals('Dispatcher.afterDispatch', $request->params['eventName']);
+
+ // Test that it is possible to skip the route connection process
+ $dispatcher = new TestDispatcher();
+ Configure::write('Dispatcher.filters', array(
+ array('callable' => array($dispatcher, 'filterTest2'), 'on' => 'before', 'priority' => 1)
+ ));
+
+ $request = new CakeRequest('/');
+ $response = $this->getMock('CakeResponse', array('send'));
+ $dispatcher->dispatch($request, $response);
+ $this->assertEmpty($dispatcher->controller);
+ $this->assertEquals(array('controller' => null, 'action' => null, 'plugin' => null), $request->params);
+
+ $dispatcher = new TestDispatcher();
+ Configure::write('Dispatcher.filters', array(
+ array('callable' => array($dispatcher, 'filterTest2'), 'on' => 'before', 'priority' => 1)
+ ));
+
+ $request = new CakeRequest('/');
+ $request->params['return'] = true;
+ $response = $this->getMock('CakeResponse', array('send'));
+ $response->body('this is a body');
+ $result = $dispatcher->dispatch($request, $response);
+ $this->assertEquals('this is a body', $result);
+
+ $request = new CakeRequest('/');
+ $response = $this->getMock('CakeResponse', array('send'));
+ $response->expects($this->once())->method('send');
+ $response->body('this is a body');
+ $result = $dispatcher->dispatch($request, $response);
+ $this->assertNull($result);
+ }
+
+/**
* testChangingParamsFromBeforeFilter method
*
* @return void
@@ -1155,23 +1324,23 @@ public function testChangingParamsFromBeforeFilter() {
$url = new CakeRequest('some_posts/index/param:value/param2:value2');
try {
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
$this->fail('No exception.');
} catch (MissingActionException $e) {
$this->assertEquals('Action SomePostsController::view() could not be found.', $e->getMessage());
}
$url = new CakeRequest('some_posts/something_else/param:value/param2:value2');
- $controller = $Dispatcher->dispatch($url, $response, array('return' => 1));
+ $Dispatcher->dispatch($url, $response, array('return' => 1));
$expected = 'SomePosts';
- $this->assertEquals($expected, $controller->name);
+ $this->assertEquals($expected, $Dispatcher->controller->name);
$expected = 'change';
- $this->assertEquals($expected, $controller->action);
+ $this->assertEquals($expected, $Dispatcher->controller->action);
$expected = array('changed');
- $this->assertSame($expected, $controller->params['pass']);
+ $this->assertSame($expected, $Dispatcher->controller->params['pass']);
}
/**
@@ -1188,6 +1357,7 @@ public function testAssets() {
'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS)
));
CakePlugin::load(array('TestPlugin', 'TestPluginTwo'));
+ Configure::write('Dispatcher.filters', array('AssetDispatcher'));
$Dispatcher = new TestDispatcher();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
@@ -1208,7 +1378,7 @@ public function testAssets() {
}
/**
- * Data provider for asset()
+ * Data provider for asset filter
*
* - theme assets.
* - plugin assets.
@@ -1306,6 +1476,7 @@ public function testAsset($url, $file) {
'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS)
));
CakePlugin::load(array('TestPlugin', 'PluginJs'));
+ Configure::write('Dispatcher.filters', array('AssetDispatcher'));
$Dispatcher = new TestDispatcher();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
@@ -1328,42 +1499,19 @@ public function testAsset($url, $file) {
* @return void
*/
public function testMissingAssetProcessor404() {
- $response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $response = $this->getMock('CakeResponse', array('send'));
$Dispatcher = new TestDispatcher();
Configure::write('Asset.filter', array(
'js' => '',
'css' => null
));
+ Configure::write('Dispatcher.filters', array('AssetDispatcher'));
- ob_start();
- $this->assertTrue($Dispatcher->asset('ccss/cake.generic.css', $response));
- $result = ob_get_clean();
+ $request = new CakeRequest('ccss/cake.generic.css');
+ $Dispatcher->dispatch($request, $response);
+ $this->assertEquals('404', $response->statusCode());
}
-/**
- * test that asset filters work for theme and plugin assets
- *
- * @return void
- */
- public function testAssetFilterForThemeAndPlugins() {
- $Dispatcher = new TestDispatcher();
- $response = $this->getMock('CakeResponse', array('_sendHeader'));
- Configure::write('Asset.filter', array(
- 'js' => '',
- 'css' => ''
- ));
- $this->assertTrue($Dispatcher->asset('theme/test_theme/ccss/cake.generic.css', $response));
-
- $this->assertTrue($Dispatcher->asset('theme/test_theme/cjs/debug_kit.js', $response));
-
- $this->assertTrue($Dispatcher->asset('test_plugin/ccss/cake.generic.css', $response));
-
- $this->assertTrue($Dispatcher->asset('test_plugin/cjs/debug_kit.js', $response));
-
- $this->assertFalse($Dispatcher->asset('css/ccss/debug_kit.css', $response));
-
- $this->assertFalse($Dispatcher->asset('js/cjs/debug_kit.js', $response));
- }
/**
* Data provider for cached actions.
@@ -1411,15 +1559,17 @@ public function testFullPageCachingDispatch($url) {
$dispatcher = new TestDispatcher();
$request = new CakeRequest($url);
- $response = new CakeResponse();
+ $response = $this->getMock('CakeResponse', array('send'));
- ob_start();
$dispatcher->dispatch($request, $response);
- $out = ob_get_clean();
+ $out = $response->body();
- ob_start();
- $dispatcher->cached($request->here());
- $cached = ob_get_clean();
+ Configure::write('Dispatcher.filters', array('CacheDispatcher'));
+ $request = new CakeRequest($url);
+ $response = $this->getMock('CakeResponse', array('send'));
+ $dispatcher = new TestDispatcher();
+ $dispatcher->dispatch($request, $response);
+ $cached = $response->body();
$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
@@ -1441,16 +1591,20 @@ public function testHttpMethodOverrides() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$dispatcher = new Dispatcher();
- $result = $dispatcher->parseParams(new CakeRequest('/posts'));
+ $request = new CakeRequest('/posts');
+ $event = new CakeEvent('DispatcherTest', $dispatcher, array('request' => $request));
+ $dispatcher->parseParams($event);
$expected = array('pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'add', '[method]' => 'POST');
foreach ($expected as $key => $value) {
- $this->assertEquals($value, $result[$key], 'Value mismatch for ' . $key . ' %s');
+ $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
}
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT';
- $result = $dispatcher->parseParams(new CakeRequest('/posts/5'));
+ $request = new CakeRequest('/posts/5');
+ $event = new CakeEvent('DispatcherTest', $dispatcher, array('request' => $request));
+ $dispatcher->parseParams($event);
$expected = array(
'pass' => array('5'),
'named' => array(),
@@ -1461,24 +1615,28 @@ public function testHttpMethodOverrides() {
'[method]' => 'PUT'
);
foreach ($expected as $key => $value) {
- $this->assertEquals($value, $result[$key], 'Value mismatch for ' . $key . ' %s');
+ $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
}
unset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
$_SERVER['REQUEST_METHOD'] = 'GET';
- $result = $dispatcher->parseParams(new CakeRequest('/posts/5'));
+ $request = new CakeRequest('/posts/5');
+ $event = new CakeEvent('DispatcherTest', $dispatcher, array('request' => $request));
+ $dispatcher->parseParams($event);
$expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'view', '[method]' => 'GET');
foreach ($expected as $key => $value) {
- $this->assertEquals($value, $result[$key], 'Value mismatch for ' . $key . ' %s');
+ $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
}
$_POST['_method'] = 'PUT';
- $result = $dispatcher->parseParams(new CakeRequest('/posts/5'));
+ $request = new CakeRequest('/posts/5');
+ $event = new CakeEvent('DispatcherTest', $dispatcher, array('request' => $request));
+ $dispatcher->parseParams($event);
$expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT');
foreach ($expected as $key => $value) {
- $this->assertEquals($value, $result[$key], 'Value mismatch for ' . $key . ' %s');
+ $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
}
$_POST['_method'] = 'POST';
@@ -1486,13 +1644,15 @@ public function testHttpMethodOverrides() {
$_POST['extra'] = 'data';
$_SERVER = array();
- $result = $dispatcher->parseParams(new CakeRequest('/posts'));
+ $request = new CakeRequest('/posts');
+ $event = new CakeEvent('DispatcherTest', $dispatcher, array('request' => $request));
+ $dispatcher->parseParams($event);
$expected = array(
'pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'add',
'[method]' => 'POST', 'data' => array('extra' => 'data', 'Post' => array('title' => 'New Post')),
);
foreach ($expected as $key => $value) {
- $this->assertEquals($value, $result[$key], 'Value mismatch for ' . $key . ' %s');
+ $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
}
unset($_POST['_method']);
View
128 lib/Cake/Test/Case/Routing/Filter/AssetDispatcherTest.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * RouterTest file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The Open Group Test Suite License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package Cake.Test.Case.Routing.Filter
+ * @since CakePHP(tm) v 2.2
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('AssetDispatcher', 'Routing/Filter');
+App::uses('CakeEvent', 'Event');
+App::uses('CakeResponse', 'Network');
+
+class AssetDispatcherTest extends CakeTestCase {
+
+/**
+ * tearDown method
+ *
+ * @return void
+ */
+ public function tearDown() {
+ Configure::write('Dispatcher.filters', array());
+ }
+
+/**
+ * test that asset filters work for theme and plugin assets
+ *
+ * @return void
+ */
+ public function testAssetFilterForThemeAndPlugins() {
+ $filter = new AssetDispatcher();
+ $response = $this->getMock('CakeResponse', array('_sendHeader'));
+ Configure::write('Asset.filter', array(
+ 'js' => '',
+ 'css' => ''
+ ));
+ App::build(array(
+ 'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS),
+ 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS)
+ ), APP::RESET);
+
+ $request = new CakeRequest('theme/test_theme/ccss/cake.generic.css');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+ $this->assertSame($response, $filter->beforeDispatch($event));
+ $this->assertTrue($event->isStopped());
+
+ $request = new CakeRequest('theme/test_theme/cjs/debug_kit.js');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+ $this->assertSame($response, $filter->beforeDispatch($event));
+ $this->assertTrue($event->isStopped());
+
+ $request = new CakeRequest('test_plugin/ccss/cake.generic.css');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+ $this->assertSame($response, $filter->beforeDispatch($event));
+ $this->assertTrue($event->isStopped());
+
+ $request = new CakeRequest('test_plugin/cjs/debug_kit.js');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+ $this->assertSame($response, $filter->beforeDispatch($event));
+ $this->assertTrue($event->isStopped());
+
+ $request = new CakeRequest('css/ccss/debug_kit.css');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+ $this->assertNull($filter->beforeDispatch($event));
+ $this->assertFalse($event->isStopped());
+
+ $request = new CakeRequest('js/cjs/debug_kit.js');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+ $this->assertNull($filter->beforeDispatch($event));
+ $this->assertFalse($event->isStopped());
+ }
+
+/**
+ * Tests that $response->checkNotModified() is called and bypasses
+ * file dispatching
+ *
+ * @return void
+ **/
+ public function testNotModified() {
+ $filter = new AssetDispatcher();
+ Configure::write('Asset.filter', array(
+ 'js' => '',
+ 'css' => ''
+ ));
+ App::build(array(
+ 'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS),
+ 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS)
+ ));
+ $time = filemtime(App::themePath('TestTheme') . 'webroot' . DS . 'img' . DS . 'cake.power.gif');
+ $time = new DateTime(date('Y-m-d H:i:s', $time), new DateTimeZone('UTC'));
+
+ $response = $this->getMock('CakeResponse', array('send', 'checkNotModified'));
+ $request = new CakeRequest('theme/test_theme/img/cake.power.gif');
+
+ $response->expects($this->once())->method('checkNotModified')
+ ->with($request)
+ ->will($this->returnValue(true));
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+
+ ob_start();
+ $this->assertSame($response, $filter->beforeDispatch($event));
+ ob_end_clean();
+ $this->assertEquals(200, $response->statusCode());
+ $this->assertEquals($time->format('D, j M Y H:i:s') . ' GMT', $response->modified());
+
+ $response = $this->getMock('CakeResponse', array('_sendHeader', 'checkNotModified'));
+ $request = new CakeRequest('theme/test_theme/img/cake.power.gif');
+
+ $response->expects($this->once())->method('checkNotModified')
+ ->with($request)
+ ->will($this->returnValue(true));
+ $response->expects($this->never())->method('send');
+ $event = new CakeEvent('DispatcherTest', $this, compact('request', 'response'));
+
+ $this->assertSame($response, $filter->beforeDispatch($event));
+ $this->assertEquals($time->format('D, j M Y H:i:s') . ' GMT', $response->modified());
+ }
+}
View
4 lib/Cake/Test/Case/View/ViewTest.php
@@ -1083,9 +1083,7 @@ public function testRenderCache() {
$f = fopen($path, 'w+');
fwrite($f, $cacheText);
fclose($f);
- ob_start();
- $View->renderCache($path, '+1 second');
- $result = ob_get_clean();
+ $result = $View->renderCache($path, '+1 second');
$this->assertRegExp('/^some cacheText/', $result);
View
33 lib/Cake/Test/test_app/Plugin/TestPlugin/Lib/Routing/Filter/Test2DispatcherFilter.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Test.test_app.Routing.Filter
+ * @since CakePHP(tm) v 2.2
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('DispatcherFilter', 'Routing');
+
+class Test2DispatcherFilter extends DispatcherFilter {
+
+ public function beforeDispatch($event) {
+ $event->data['response']->statusCode(500);
+ $event->stopPropagation();
+ return $event->data['response'];
+ }
+
+ public function afterDispatch($event) {
+ $event->data['response']->statusCode(200);
+ }
+
+}
View
31 lib/Cake/Test/test_app/Plugin/TestPlugin/Lib/Routing/Filter/TestDispatcherFilter.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Test.test_app.Routing.Filter
+ * @since CakePHP(tm) v 2.2
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('DispatcherFilter', 'Routing');
+
+class TestDispatcherFilter extends DispatcherFilter {
+
+ public function beforeDispatch($event) {
+ $event->data['request']->params['altered'] = true;
+ }
+
+ public function afterDispatch($event) {
+ $event->data['response']->statusCode(304);