From c71f919819e69e19aaf2653910686baaf57c1371 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 5 Oct 2014 13:07:20 -0400 Subject: [PATCH 1/4] Add Model.initialize event. This event follows the same pattern as Controller.initialize where it is fired *after* the initialize() method and after other subscribers have been attached. The initialize() method is *not* an event subscriber as it is intended to be a post-constructor hook method like Controller::initialize(). This will likely result in Component::initialize() being modified to be conformant. There have been a number of reported issues in the past around Component::initialize() not being called. It would be nice to put those issues to rest and have consistency across the framework. --- src/ORM/Table.php | 1 + tests/TestCase/ORM/TableTest.php | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/ORM/Table.php b/src/ORM/Table.php index 86a179c8161..6d6329fd2ee 100644 --- a/src/ORM/Table.php +++ b/src/ORM/Table.php @@ -230,6 +230,7 @@ public function __construct(array $config = []) { $this->initialize($config); $this->_eventManager->attach($this); + $this->dispatchEvent('Model.initialize'); } /** diff --git a/tests/TestCase/ORM/TableTest.php b/tests/TestCase/ORM/TableTest.php index 0f0971d1ffd..5945dbe1343 100644 --- a/tests/TestCase/ORM/TableTest.php +++ b/tests/TestCase/ORM/TableTest.php @@ -19,6 +19,7 @@ use Cake\Database\Expression\QueryExpression; use Cake\Database\TypeMap; use Cake\Datasource\ConnectionManager; +use Cake\Event\EventManager; use Cake\I18n\Time; use Cake\ORM\Table; use Cake\ORM\TableRegistry; @@ -81,6 +82,11 @@ public function setUp() { ]); } +/** + * teardown method + * + * @return void + */ public function tearDown() { parent::tearDown(); TableRegistry::clear(); @@ -1902,6 +1908,9 @@ public function testDeleteCallbacks() { ->method('attach'); $mock->expects($this->at(1)) + ->method('dispatch'); + + $mock->expects($this->at(2)) ->method('dispatch') ->with($this->logicalAnd( $this->attributeEqualTo('_name', 'Model.beforeDelete'), @@ -1911,7 +1920,7 @@ public function testDeleteCallbacks() { ) )); - $mock->expects($this->at(2)) + $mock->expects($this->at(3)) ->method('dispatch') ->with($this->logicalAnd( $this->attributeEqualTo('_name', 'Model.afterDelete'), @@ -1936,7 +1945,7 @@ public function testDeleteBeforeDeleteAbort() { $options = new \ArrayObject(['atomic' => true, 'cascade' => true]); $mock = $this->getMock('Cake\Event\EventManager'); - $mock->expects($this->once()) + $mock->expects($this->at(2)) ->method('dispatch') ->will($this->returnCallback(function ($event) { $event->stopPropagation(); @@ -1958,7 +1967,7 @@ public function testDeleteBeforeDeleteReturnResult() { $options = new \ArrayObject(['atomic' => true, 'cascade' => true]); $mock = $this->getMock('Cake\Event\EventManager'); - $mock->expects($this->once()) + $mock->expects($this->at(2)) ->method('dispatch') ->will($this->returnCallback(function ($event) { $event->stopPropagation(); @@ -3589,4 +3598,21 @@ function ($article) { $this->assertEquals(2, $article->author_id); } +/** + * Test that creating a table fires the initialize event. + * + * @return void + */ + public function testInitializeEvent() { + $count = 0; + $cb = function ($event) use (&$count){ + $count++; + }; + EventManager::instance()->attach($cb, 'Model.initialize'); + $articles = TableRegistry::get('Articles'); + + $this->assertEquals(1, $count, 'Callback should be called'); + EventManager::instance()->detach($cb, 'Model.initialize'); + } + } From 293b4b0fccad2e30ed9c9fefb797ba9b708a7a51 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 5 Oct 2014 22:57:57 -0400 Subject: [PATCH 2/4] Make Component::initialize() work like other initialize() methods. Make Component::initialize() consistent with Controller and Table, this makes the framework easier to understand as methods with the same name work consistently everywhere. While I'm not totally sure about what to do with the Controller.initialize event hook in components, I feel this change is a good one even though it breaks existing API compatibility. The primary use of this event hook was RequestHandler which has been updated to not rely on the event hook. I've also ensure that Controllers always have a request and response object as that was causing me some grief and logically makes sense. --- src/Controller/Component.php | 16 +- .../Component/RequestHandlerComponent.php | 104 +++++++------ src/Controller/Controller.php | 12 +- .../Component/RequestHandlerComponentTest.php | 139 +++++++++--------- tests/TestCase/Controller/ControllerTest.php | 4 +- .../TestCase/Error/ExceptionRendererTest.php | 4 +- .../Controller/Component/OrangeComponent.php | 7 +- 7 files changed, 154 insertions(+), 132 deletions(-) diff --git a/src/Controller/Component.php b/src/Controller/Component.php index c161812084a..da4fc636967 100644 --- a/src/Controller/Component.php +++ b/src/Controller/Component.php @@ -28,8 +28,6 @@ * Components can provide several callbacks that are fired at various stages of the request * cycle. The available callbacks are: * - * - `initialize(Event $event)` - * Called before the controller's beforeFilter method. * - `startup(Event $event)` * Called after the controller's beforeFilter method, and before the * controller action is called. @@ -99,6 +97,18 @@ public function __construct(ComponentRegistry $registry, array $config = []) { if (!empty($this->components)) { $this->_componentMap = $registry->normalizeArray($this->components); } + $this->initialize($config); + } + +/** + * Constructor hook method. + * + * Implement this method to avoid having to overwrite + * the constructor and call parent. + * + * @return void + */ + public function initialize(array $config) { } /** @@ -131,7 +141,7 @@ public function __get($name) { */ public function implementedEvents() { $eventMap = [ - 'Controller.initialize' => 'initialize', + // 'Controller.initialize' => 'initialize', 'Controller.startup' => 'startup', 'Controller.beforeRender' => 'beforeRender', 'Controller.beforeRedirect' => 'beforeRedirect', diff --git a/src/Controller/Component/RequestHandlerComponent.php b/src/Controller/Component/RequestHandlerComponent.php index 503a1770614..8d8d9602c3b 100644 --- a/src/Controller/Component/RequestHandlerComponent.php +++ b/src/Controller/Component/RequestHandlerComponent.php @@ -120,10 +120,6 @@ class RequestHandlerComponent extends Component { public function __construct(ComponentRegistry $registry, array $config = array()) { parent::__construct($registry, $config); $this->addInputType('xml', array(array($this, 'convertXml'))); - - $Controller = $registry->getController(); - $this->request = $Controller->request; - $this->response = $Controller->response; } /** @@ -133,7 +129,6 @@ public function __construct(ComponentRegistry $registry, array $config = array() */ public function implementedEvents() { return [ - 'Controller.initialize' => 'initialize', 'Controller.startup' => 'startup', 'Controller.beforeRender' => 'beforeRender', 'Controller.beforeRedirect' => 'beforeRedirect', @@ -147,24 +142,27 @@ public function implementedEvents() { * of an ajax request indicated using the second header based method above, the type must have * been configured in {@link Cake\Routing\Router}. * - * @param Event $event The initialize event that was fired. + * @param array $config The config data. * @return void * @see Router::extensions() */ - public function initialize(Event $event) { - if (isset($this->request->params['_ext'])) { - $this->ext = $this->request->params['_ext']; + public function initialize(array $config) { + $controller = $this->_registry->getController(); + $request = $this->request = $controller->request; + $response = $this->response = $controller->response; + + if (isset($request->params['_ext'])) { + $this->ext = $request->params['_ext']; } if (empty($this->ext) || in_array($this->ext, array('html', 'htm'))) { - $this->_setExtension(); + $this->_setExtension($request, $this->response); } - if (empty($this->ext) && $this->request->is('ajax')) { + if (empty($this->ext) && $request->is('ajax')) { $this->ext = 'ajax'; } - $classMap = $this->_config['viewClassMap']; - if ($classMap) { - $this->viewClassMap($classMap); + if ($this->_config['viewClassMap']) { + $this->viewClassMap($this->_config['viewClassMap']); } } @@ -179,15 +177,17 @@ public function initialize(Event $event) { * If html is one of the preferred types, no content type will be set, this * is to avoid issues with browsers that prefer html and several other content types. * + * @param \Cake\Network\Request $request The request instance. + * @param \Cake\Network\Response $response The response instance. * @return void */ - protected function _setExtension() { - $accept = $this->request->parseAccept(); + protected function _setExtension($request, $response) { + $accept = $request->parseAccept(); if (empty($accept)) { return; } - $accepts = $this->response->mapType($accept); + $accepts = $response->mapType($accept); $preferedTypes = current($accepts); if (array_intersect($preferedTypes, array('html', 'xhtml'))) { return; @@ -267,7 +267,6 @@ public function convertXml($xml) { /** * Handles (fakes) redirects for Ajax requests using requestAction() - * Modifies the $_POST and $_SERVER['REQUEST_METHOD'] to simulate a new GET request. * * @param Event $event The Controller.beforeRedirect event. * @param string|array $url A string or array containing the redirect location @@ -275,21 +274,24 @@ public function convertXml($xml) { * @return void */ public function beforeRedirect(Event $event, $url, Response $response) { - if (!$this->request->is('ajax')) { + $request = $this->request; + if (!$request->is('ajax')) { return; } if (empty($url)) { return; } - $_SERVER['REQUEST_METHOD'] = 'GET'; - foreach ($_POST as $key => $val) { - unset($_POST[$key]); - } if (is_array($url)) { $url = Router::url($url + array('base' => false)); } $controller = $event->subject(); - $response->body($controller->requestAction($url, array('return', 'bare' => false))); + $response->body($controller->requestAction($url, [ + 'return', + 'bare' => false, + 'environment' => [ + 'REQUEST_METHOD' => 'GET' + ] + ])); $response->send(); $response->stop(); } @@ -304,7 +306,9 @@ public function beforeRedirect(Event $event, $url, Response $response) { * @return bool false if the render process should be aborted */ public function beforeRender(Event $event) { - if ($this->_config['checkHttpCache'] && $this->response->checkNotModified($this->request)) { + $response = $this->response; + $request = $this->request; + if ($this->_config['checkHttpCache'] && $response->checkNotModified($request)) { return false; } } @@ -343,7 +347,8 @@ public function isAtom() { * @return bool True if user agent is a mobile web browser */ public function isMobile() { - return $this->request->is('mobile') || $this->accepts('wap'); + $request = $this->request; + return $request->is('mobile') || $this->accepts('wap'); } /** @@ -378,10 +383,12 @@ public function isWap() { * if the client accepts one or more elements in the array. */ public function accepts($type = null) { - $accepted = $this->request->accepts(); + $request = $this->request; + $response = $this->response; + $accepted = $request->accepts(); if (!$type) { - return $this->response->mapType($accepted); + return $response->mapType($accepted); } if (is_array($type)) { foreach ($type as $t) { @@ -407,9 +414,10 @@ public function accepts($type = null) { * in the request content type will be returned. */ public function requestedWith($type = null) { - if (!$this->request->is('post') && - !$this->request->is('put') && - !$this->request->is('delete') + $request = $this->request; + if (!$request->is('post') && + !$request->is('put') && + !$request->is('delete') ) { return null; } @@ -422,15 +430,16 @@ public function requestedWith($type = null) { return false; } - list($contentType) = explode(';', $this->request->env('CONTENT_TYPE')); + list($contentType) = explode(';', $request->env('CONTENT_TYPE')); if ($contentType === '') { - list($contentType) = explode(';', $this->request->header('CONTENT_TYPE')); + list($contentType) = explode(';', $request->header('CONTENT_TYPE')); } + $response = $this->response; if (!$type) { - return $this->response->mapType($contentType); + return $response->mapType($contentType); } if (is_string($type)) { - return ($type === $this->response->mapType($contentType)); + return ($type === $response->mapType($contentType)); } } @@ -451,12 +460,14 @@ public function requestedWith($type = null) { * If no type is provided the first preferred type is returned. */ public function prefers($type = null) { - $acceptRaw = $this->request->parseAccept(); + $request = $this->request; + $response = $this->response; + $acceptRaw = $request->parseAccept(); if (empty($acceptRaw)) { return $this->ext; } - $accepts = $this->response->mapType(array_shift($acceptRaw)); + $accepts = $response->mapType(array_shift($acceptRaw)); if (!$type) { if (empty($this->ext) && !empty($accepts)) { @@ -535,7 +546,8 @@ public function renderAs(Controller $controller, $type, array $options = array() $controller->layoutPath = $type; } - if ($this->response->getMimeType($type)) { + $response = $this->response; + if ($response->getMimeType($type)) { $this->respondAs($type, $options); } @@ -566,8 +578,10 @@ public function respondAs($type, array $options = array()) { $options += $defaults; $cType = $type; + $request = $this->request; + $response = $this->response; if (strpos($type, '/') === false) { - $cType = $this->response->getMimeType($type); + $cType = $response->getMimeType($type); } if (is_array($cType)) { if (isset($cType[$options['index']])) { @@ -585,13 +599,13 @@ public function respondAs($type, array $options = array()) { return false; } if (empty($this->request->params['requested'])) { - $this->response->type($cType); + $response->type($cType); } if (!empty($options['charset'])) { - $this->response->charset($options['charset']); + $response->charset($options['charset']); } if (!empty($options['attachment'])) { - $this->response->download($options['attachment']); + $response->download($options['attachment']); } return true; } @@ -603,7 +617,8 @@ public function respondAs($type, array $options = array()) { * otherwise null */ public function responseType() { - return $this->response->mapType($this->response->type()); + $response = $this->response; + return $response->mapType($response->type()); } /** @@ -617,7 +632,8 @@ public function mapAlias($alias) { if (is_array($alias)) { return array_map(array($this, 'mapAlias'), $alias); } - $type = $this->response->getMimeType($alias); + $response = $this->response; + $type = $response->getMimeType($alias); if ($type) { if (is_array($type)) { return $type[0]; diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php index abda2463404..9e8887a82e2 100644 --- a/src/Controller/Controller.php +++ b/src/Controller/Controller.php @@ -261,12 +261,16 @@ public function __construct(Request $request = null, Response $response = null, $this->viewPath = $viewPath; } - if ($request instanceof Request) { - $this->setRequest($request); + if (!($request instanceof Request)) { + $request = new Request(); } - if ($response instanceof Response) { - $this->response = $response; + $this->setRequest($request); + + if (!($response instanceof Response)) { + $response = new Response(); } + $this->response = $response; + if ($eventManager) { $this->eventManager($eventManager); } diff --git a/tests/TestCase/Controller/Component/RequestHandlerComponentTest.php b/tests/TestCase/Controller/Component/RequestHandlerComponentTest.php index 356ec294d01..aeec0b26871 100644 --- a/tests/TestCase/Controller/Component/RequestHandlerComponentTest.php +++ b/tests/TestCase/Controller/Component/RequestHandlerComponentTest.php @@ -69,6 +69,7 @@ protected function _init() { $response = $this->getMock('Cake\Network\Response', array('_sendHeader', 'stop')); $this->Controller = new RequestHandlerTestController($request, $response); $this->RequestHandler = new RequestHandlerComponent($this->Controller->components()); + $this->request = $request; Router::scope('/', function ($routes) { $routes->extensions('json'); @@ -109,10 +110,9 @@ public function testConstructorConfig() { * @return void */ public function testInitializeCallback() { - $event = new Event('Controller.initialize', $this->Controller); $this->assertNull($this->RequestHandler->ext); $this->Controller->request->params['_ext'] = 'rss'; - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertEquals('rss', $this->RequestHandler->ext); } @@ -122,13 +122,11 @@ public function testInitializeCallback() { * @return void */ public function testInitializeContentTypeSettingExt() { - $event = new Event('Controller.initialize', $this->Controller); - $_SERVER['HTTP_ACCEPT'] = 'application/json'; + $this->request->env('HTTP_ACCEPT', 'application/json'); Router::extensions('json', false); - $this->assertNull($this->RequestHandler->ext); - - $this->RequestHandler->initialize($event); + $this->RequestHandler->ext = null; + $this->RequestHandler->initialize([]); $this->assertEquals('json', $this->RequestHandler->ext); } @@ -138,13 +136,12 @@ public function testInitializeContentTypeSettingExt() { * @return void */ public function testInitializeContentTypeWithjQueryAccept() { - $_SERVER['HTTP_ACCEPT'] = 'application/json, application/javascript, */*; q=0.01'; - $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; - $event = new Event('Controller.initialize', $this->Controller); - $this->assertNull($this->RequestHandler->ext); + $this->request->env('HTTP_ACCEPT', 'application/json, application/javascript, */*; q=0.01'); + $this->request->env('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); + $this->RequestHandler->ext = null; Router::extensions('json', false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertEquals('json', $this->RequestHandler->ext); } @@ -154,12 +151,10 @@ public function testInitializeContentTypeWithjQueryAccept() { * @return void */ public function testInitializeContentTypeWithjQueryTextPlainAccept() { - $_SERVER['HTTP_ACCEPT'] = 'text/plain, */*; q=0.01'; - $event = new Event('Controller.initialize', $this->Controller); - $this->assertNull($this->RequestHandler->ext); Router::extensions('csv', false); + $this->request->env('HTTP_ACCEPT', 'text/plain, */*; q=0.01'); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertNull($this->RequestHandler->ext); } @@ -170,12 +165,11 @@ public function testInitializeContentTypeWithjQueryTextPlainAccept() { * @return void */ public function testInitializeContentTypeWithjQueryAcceptAndMultiplesExtensions() { - $_SERVER['HTTP_ACCEPT'] = 'application/json, application/javascript, */*; q=0.01'; - $event = new Event('Controller.initialize', $this->Controller); - $this->assertNull($this->RequestHandler->ext); + $this->request->env('HTTP_ACCEPT', 'application/json, application/javascript, */*; q=0.01'); + $this->RequestHandler->ext = null; Router::extensions(['rss', 'json'], false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertEquals('json', $this->RequestHandler->ext); } @@ -186,11 +180,10 @@ public function testInitializeContentTypeWithjQueryAcceptAndMultiplesExtensions( */ public function testInitializeNoContentTypeWithSingleAccept() { $_SERVER['HTTP_ACCEPT'] = 'application/json, text/html, */*; q=0.01'; - $event = new Event('Controller.initialize', $this->Controller); $this->assertNull($this->RequestHandler->ext); Router::extensions('json', false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertNull($this->RequestHandler->ext); } @@ -203,18 +196,20 @@ public function testInitializeNoContentTypeWithSingleAccept() { * @return void */ public function testInitializeNoContentTypeWithMultipleAcceptedTypes() { - $_SERVER['HTTP_ACCEPT'] = 'application/json, application/javascript, application/xml, */*; q=0.01'; - $event = new Event('Controller.initialize', $this->Controller); - $this->assertNull($this->RequestHandler->ext); + $this->request->env( + 'HTTP_ACCEPT', + 'application/json, application/javascript, application/xml, */*; q=0.01' + ); + $this->RequestHandler->ext = null; Router::extensions(['xml', 'json'], false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertEquals('xml', $this->RequestHandler->ext); $this->RequestHandler->ext = null; Router::extensions(array('json', 'xml'), false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertEquals('json', $this->RequestHandler->ext); } @@ -224,12 +219,14 @@ public function testInitializeNoContentTypeWithMultipleAcceptedTypes() { * @return void */ public function testInitializeContentTypeWithMultipleAcceptedTypes() { - $_SERVER['HTTP_ACCEPT'] = 'text/csv;q=1.0, application/json;q=0.8, application/xml;q=0.7'; - $event = new Event('Controller.initialize', $this->Controller); - $this->assertNull($this->RequestHandler->ext); + $this->request->env( + 'HTTP_ACCEPT', + 'text/csv;q=1.0, application/json;q=0.8, application/xml;q=0.7' + ); + $this->RequestHandler->ext = null; Router::extensions(['xml', 'json'], false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertEquals('json', $this->RequestHandler->ext); } @@ -239,12 +236,14 @@ public function testInitializeContentTypeWithMultipleAcceptedTypes() { * @return void */ public function testInitializeAmbiguousAndroidAccepts() { - $_SERVER['HTTP_ACCEPT'] = 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'; - $event = new Event('Controller.initialize', $this->Controller); - $this->assertNull($this->RequestHandler->ext); + $this->request->env( + 'HTTP_ACCEPT', + 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5' + ); + $this->RequestHandler->ext = null; Router::extensions(['html', 'xml'], false); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertNull($this->RequestHandler->ext); } @@ -257,8 +256,7 @@ public function testInititalizeFirefoxHeaderNotXml() { $_SERVER['HTTP_ACCEPT'] = 'text/html,application/xhtml+xml,application/xml;image/png,image/jpeg,image/*;q=0.9,*/*;q=0.8'; Router::extensions(['xml', 'json'], false); - $event = new Event('Controller.initialize', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertNull($this->RequestHandler->ext); } @@ -268,7 +266,6 @@ public function testInititalizeFirefoxHeaderNotXml() { * @return void */ public function testInitializeContentTypeAndExtensionMismatch() { - $event = new Event('Controller.initialize', $this->Controller); $this->assertNull($this->RequestHandler->ext); $extensions = Router::extensions(); Router::extensions('xml', false); @@ -278,7 +275,7 @@ public function testInitializeContentTypeAndExtensionMismatch() { ->method('accepts') ->will($this->returnValue(array('application/json'))); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->assertNull($this->RequestHandler->ext); call_user_func_array(array('Cake\Routing\Router', 'extensions'), [$extensions, false]); @@ -290,9 +287,8 @@ public function testInitializeContentTypeAndExtensionMismatch() { * @return void */ public function testViewClassMap() { - $event = new Event('Controller.initialize', $this->Controller); $this->RequestHandler->config(array('viewClassMap' => array('json' => 'CustomJson'))); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $result = $this->RequestHandler->viewClassMap(); $expected = array( 'json' => 'CustomJson', @@ -320,10 +316,9 @@ public function testViewClassMap() { * @return void */ public function testDisabling() { - $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; - $this->_init(); + $this->request->env('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); $event = new Event('Controller.startup', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->Controller->beforeFilter($event); $this->RequestHandler->startup($event); $this->assertEquals(true, $this->Controller->request->params['isAjax']); @@ -336,8 +331,8 @@ public function testDisabling() { */ public function testAutoAjaxLayout() { $event = new Event('Controller.startup', $this->Controller); - $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; - $this->RequestHandler->initialize($event); + $this->request->env('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertEquals($this->Controller->viewClass, 'Cake\View\AjaxView'); $view = $this->Controller->createView(); @@ -345,11 +340,9 @@ public function testAutoAjaxLayout() { $this->_init(); $this->Controller->request->params['_ext'] = 'js'; - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertNotEquals($this->Controller->viewClass, 'Cake\View\AjaxView'); - - unset($_SERVER['HTTP_X_REQUESTED_WITH']); } /** @@ -361,7 +354,7 @@ public function testJsonViewLoaded() { Router::extensions(['json', 'xml', 'ajax'], false); $this->Controller->request->params['_ext'] = 'json'; $event = new Event('Controller.startup', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertEquals('Cake\View\JsonView', $this->Controller->viewClass); $view = $this->Controller->createView(); @@ -378,7 +371,7 @@ public function testXmlViewLoaded() { Router::extensions(['json', 'xml', 'ajax'], false); $this->Controller->request->params['_ext'] = 'xml'; $event = new Event('Controller.startup', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertEquals('Cake\View\XmlView', $this->Controller->viewClass); $view = $this->Controller->createView(); @@ -395,7 +388,7 @@ public function testAjaxViewLoaded() { Router::extensions(['json', 'xml', 'ajax'], false); $this->Controller->request->params['_ext'] = 'ajax'; $event = new Event('Controller.startup', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertEquals('Cake\View\AjaxView', $this->Controller->viewClass); $view = $this->Controller->createView(); @@ -411,7 +404,7 @@ public function testNoViewClassExtension() { Router::extensions(['json', 'xml', 'ajax', 'csv'], false); $this->Controller->request->params['_ext'] = 'csv'; $event = new Event('Controller.startup', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertEquals('RequestHandlerTest' . DS . 'csv', $this->Controller->viewPath); $this->assertEquals('csv', $this->Controller->layoutPath); @@ -462,8 +455,8 @@ public function testStartupCustomTypeProcess() { ->method('_readInput') ->will($this->returnValue('"A","csv","string"')); $this->RequestHandler->addInputType('csv', array('str_getcsv')); - $this->RequestHandler->request->env('REQUEST_METHOD', 'POST'); - $this->RequestHandler->request->env('CONTENT_TYPE', 'text/csv'); + $this->request->env('REQUEST_METHOD', 'POST'); + $this->request->env('CONTENT_TYPE', 'text/csv'); $this->RequestHandler->startup($event); $expected = array( 'A', 'csv', 'string' @@ -478,7 +471,7 @@ public function testStartupCustomTypeProcess() { */ public function testNonAjaxRedirect() { $event = new Event('Controller.startup', $this->Controller); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertNull($this->RequestHandler->beforeRedirect($event, '/', $this->Controller->response)); } @@ -496,7 +489,7 @@ public function testAjaxRedirectWithNoUrl() { $this->Controller->response->expects($this->never()) ->method('body'); - $this->RequestHandler->initialize($event); + $this->RequestHandler->initialize([]); $this->RequestHandler->startup($event); $this->assertNull($this->RequestHandler->beforeRedirect($event, null, $this->Controller->response)); } @@ -610,11 +603,11 @@ public function testRenderAsCalledTwice() { * @return void */ public function testRequestContentTypes() { - $this->RequestHandler->request->env('REQUEST_METHOD', 'GET'); + $this->request->env('REQUEST_METHOD', 'GET'); $this->assertNull($this->RequestHandler->requestedWith()); - $this->RequestHandler->request->env('REQUEST_METHOD', 'POST'); - $this->RequestHandler->request->env('CONTENT_TYPE', 'application/json'); + $this->request->env('REQUEST_METHOD', 'POST'); + $this->request->env('CONTENT_TYPE', 'application/json'); $this->assertEquals('json', $this->RequestHandler->requestedWith()); $result = $this->RequestHandler->requestedWith(array('json', 'xml')); @@ -623,12 +616,12 @@ public function testRequestContentTypes() { $result = $this->RequestHandler->requestedWith(array('rss', 'atom')); $this->assertFalse($result); - $this->RequestHandler->request->env('REQUEST_METHOD', 'DELETE'); + $this->request->env('REQUEST_METHOD', 'DELETE'); $this->assertEquals('json', $this->RequestHandler->requestedWith()); - $this->RequestHandler->request->env('REQUEST_METHOD', 'POST'); - $this->RequestHandler->request->env('CONTENT_TYPE', ''); - $this->RequestHandler->request->env('HTTP_CONTENT_TYPE', 'application/json'); + $this->request->env('REQUEST_METHOD', 'POST'); + $this->request->env('CONTENT_TYPE', ''); + $this->request->env('HTTP_CONTENT_TYPE', 'application/json'); $result = $this->RequestHandler->requestedWith(array('json', 'xml')); $this->assertEquals('json', $result); @@ -636,21 +629,21 @@ public function testRequestContentTypes() { $result = $this->RequestHandler->requestedWith(array('rss', 'atom')); $this->assertFalse($result); - $this->RequestHandler->request->env('HTTP_ACCEPT', 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'); + $this->request->env('HTTP_ACCEPT', 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'); $this->assertTrue($this->RequestHandler->isXml()); $this->assertFalse($this->RequestHandler->isAtom()); $this->assertFalse($this->RequestHandler->isRSS()); - $this->RequestHandler->request->env('HTTP_ACCEPT', 'application/atom+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'); + $this->request->env('HTTP_ACCEPT', 'application/atom+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'); $this->assertTrue($this->RequestHandler->isAtom()); $this->assertFalse($this->RequestHandler->isRSS()); - $this->RequestHandler->request->env('HTTP_ACCEPT', 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'); + $this->request->env('HTTP_ACCEPT', 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'); $this->assertFalse($this->RequestHandler->isAtom()); $this->assertTrue($this->RequestHandler->isRSS()); $this->assertFalse($this->RequestHandler->isWap()); - $this->RequestHandler->request->env('HTTP_ACCEPT', 'text/vnd.wap.wml,text/html,text/plain,image/png,*/*'); + $this->request->env('HTTP_ACCEPT', 'text/vnd.wap.wml,text/html,text/plain,image/png,*/*'); $this->assertTrue($this->RequestHandler->isWap()); } @@ -706,11 +699,11 @@ public function testMapAlias() { * @return void */ public function testAccepts() { - $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'; + $this->request->env('HTTP_ACCEPT', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'); $this->assertTrue($this->RequestHandler->accepts(array('js', 'xml', 'html'))); $this->assertFalse($this->RequestHandler->accepts(array('gif', 'jpeg', 'foo'))); - $_SERVER['HTTP_ACCEPT'] = '*/*;q=0.5'; + $this->request->env('HTTP_ACCEPT', '*/*;q=0.5'); $this->assertFalse($this->RequestHandler->accepts('rss')); } @@ -720,7 +713,7 @@ public function testAccepts() { * @return void */ public function testPrefers() { - $this->RequestHandler->request->env( + $this->request->env( 'HTTP_ACCEPT', 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*' ); @@ -735,13 +728,13 @@ public function testPrefers() { $this->assertFalse($this->RequestHandler->prefers(array('html')), 'No match with ext, return false.'); $this->_init(); - $this->RequestHandler->request->env( + $this->request->env( 'HTTP_ACCEPT', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5' ); $this->assertEquals('xml', $this->RequestHandler->prefers()); - $this->RequestHandler->request->env('HTTP_ACCEPT', '*/*;q=0.5'); + $this->request->env('HTTP_ACCEPT', '*/*;q=0.5'); $this->assertEquals('html', $this->RequestHandler->prefers()); $this->assertFalse($this->RequestHandler->prefers('rss')); } diff --git a/tests/TestCase/Controller/ControllerTest.php b/tests/TestCase/Controller/ControllerTest.php index c50db7fb3d0..db7af5939cb 100644 --- a/tests/TestCase/Controller/ControllerTest.php +++ b/tests/TestCase/Controller/ControllerTest.php @@ -148,10 +148,10 @@ public function beforeRedirect() { /** * initialize method * - * @param Event $event + * @param array $config * @return void */ - public function initialize(Event $event) { + public function initialize(array $config) { } /** diff --git a/tests/TestCase/Error/ExceptionRendererTest.php b/tests/TestCase/Error/ExceptionRendererTest.php index 9a9c1e91253..748e121f55c 100644 --- a/tests/TestCase/Error/ExceptionRendererTest.php +++ b/tests/TestCase/Error/ExceptionRendererTest.php @@ -56,10 +56,10 @@ class BlueberryComponent extends Component { /** * initialize method * - * @param Event $event + * @param array $config * @return void */ - public function initialize(Event $event) { + public function initialize(array $config) { $this->testName = 'BlueberryComponent'; } diff --git a/tests/test_app/TestApp/Controller/Component/OrangeComponent.php b/tests/test_app/TestApp/Controller/Component/OrangeComponent.php index b7155b27948..424412a26fe 100644 --- a/tests/test_app/TestApp/Controller/Component/OrangeComponent.php +++ b/tests/test_app/TestApp/Controller/Component/OrangeComponent.php @@ -33,12 +33,11 @@ class OrangeComponent extends Component { /** * initialize method * - * @param Event $event - * @param Controller $controller + * @param array $config * @return void */ - public function initialize(Event $event) { - $this->Controller = $event->subject(); + public function initialize(array $config) { + $this->Controller = $this->_registry->getController(); $this->Banana->testField = 'OrangeField'; } From 946bf55d296898e84734f0ebb1f396c65eb39061 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 5 Oct 2014 23:04:45 -0400 Subject: [PATCH 3/4] Add Component::beforeFilter This method is an event listener that listens to the Controller.initialize event. I decided to copy the controller method name so there was some semblance of consistency. This may be a bad idea though. --- src/Controller/Component.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Component.php b/src/Controller/Component.php index da4fc636967..a2248a5b3bd 100644 --- a/src/Controller/Component.php +++ b/src/Controller/Component.php @@ -141,7 +141,7 @@ public function __get($name) { */ public function implementedEvents() { $eventMap = [ - // 'Controller.initialize' => 'initialize', + 'Controller.initialize' => 'beforeFilter', 'Controller.startup' => 'startup', 'Controller.beforeRender' => 'beforeRender', 'Controller.beforeRedirect' => 'beforeRedirect', From 65956280bf3f09a3e14d797eb80bcd6c5b022479 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Mon, 6 Oct 2014 19:54:05 -0400 Subject: [PATCH 4/4] Fix PHPCS error and add doc block. --- src/Controller/Component.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Controller/Component.php b/src/Controller/Component.php index a2248a5b3bd..4ed7f6f7677 100644 --- a/src/Controller/Component.php +++ b/src/Controller/Component.php @@ -23,11 +23,19 @@ * controller logic that can be composed into a controller. Components also * provide request life-cycle callbacks for injecting logic at specific points. * + * ## Initialize hook + * + * Like Controller and Table, this class has an initialize() hook that you can use + * to add custom 'constructor' logic. It is important to remember that each request + * (and sub-request) will only make one instance of any given component. + * * ## Life cycle callbacks * * Components can provide several callbacks that are fired at various stages of the request * cycle. The available callbacks are: * + * - `beforeFilter(Event $event)` + * Called before the controller's beforeFilter method by default. * - `startup(Event $event)` * Called after the controller's beforeFilter method, and before the * controller action is called. @@ -106,6 +114,7 @@ public function __construct(ComponentRegistry $registry, array $config = []) { * Implement this method to avoid having to overwrite * the constructor and call parent. * + * @param array $config The configuration array this component is using. * @return void */ public function initialize(array $config) {