mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2024-11-15 03:18:26 +00:00
Introducing dispatcher filters and adding tests for them
This commit is contained in:
parent
05b88f3f0e
commit
565a58f784
6 changed files with 347 additions and 2 deletions
|
@ -438,6 +438,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.
|
||||
*
|
||||
|
|
|
@ -68,6 +68,7 @@ class Dispatcher implements CakeEventListener {
|
|||
if (!$this->_eventManager) {
|
||||
$this->_eventManager = new CakeEventManager();
|
||||
$this->_eventManager->attach($this);
|
||||
$this->_attachFilters($this->_eventManager);
|
||||
}
|
||||
return $this->_eventManager;
|
||||
}
|
||||
|
@ -87,6 +88,41 @@ class Dispatcher implements CakeEventListener {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['priority'] = $filter['priority'];
|
||||
}
|
||||
$manager->attach($filter['callable'], 'Dispatcher.' . $on, $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.
|
||||
|
@ -102,7 +138,7 @@ class Dispatcher implements CakeEventListener {
|
|||
* @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()) {
|
||||
|
@ -111,7 +147,10 @@ class Dispatcher implements CakeEventListener {
|
|||
|
||||
$request = $beforeEvent->data['request'];
|
||||
if ($beforeEvent->result instanceof CakeResponse) {
|
||||
$beforeEvent->result->send();
|
||||
if (isset($request->params['return'])) {
|
||||
return $response->body();
|
||||
}
|
||||
$response->send();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
85
lib/Cake/Routing/DispatcherFilter.php
Normal file
85
lib/Cake/Routing/DispatcherFilter.php
Normal file
|
@ -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.Event
|
||||
*/
|
||||
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.before' => array('callable' => 'preDispatch', 'priority' => $this->priority),
|
||||
'Dispatcher.after' => array('callable' => 'postDispatch', '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 preDispatch($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 postDispatch($event) {}
|
||||
}
|
|
@ -63,6 +63,27 @@ class TestDispatcher extends Dispatcher {
|
|||
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'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,6 +584,7 @@ class DispatcherTest extends CakeTestCase {
|
|||
Configure::write('App', $this->_app);
|
||||
Configure::write('Cache', $this->_cache);
|
||||
Configure::write('debug', $this->_debug);
|
||||
Configure::write('Dispatcher.filters', array());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -761,6 +783,7 @@ class DispatcherTest extends CakeTestCase {
|
|||
$Dispatcher->dispatch($url, $response, array('return' => 1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* testDispatch method
|
||||
*
|
||||
|
@ -1167,6 +1190,129 @@ class DispatcherTest extends CakeTestCase {
|
|||
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.before', $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.after', $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
|
||||
*
|
||||
|
|
|
@ -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 preDispatch($event) {
|
||||
$event->data['response']->statusCode(500);
|
||||
$event->stopPropagation();
|
||||
return $event->data['response'];
|
||||
}
|
||||
|
||||
public function postDispatch($event) {
|
||||
$event->data['response']->statusCode(200);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 preDispatch($event) {
|
||||
$event->data['request']->params['altered'] = true;
|
||||
}
|
||||
|
||||
public function postDispatch($event) {
|
||||
$event->data['response']->statusCode(304);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue