Skip to content
This repository
Browse code

Merge branch 'request-response' into 2.0

  • Loading branch information...
commit 3589a1732145a324ccc68653993e72cf168cedc5 2 parents 63ea4ef + ec3de84
Mark Story authored July 23, 2011
12  app/Config/routes.php
@@ -30,3 +30,15 @@
30 30
  * ...and connect the rest of 'Pages' controller's urls.
31 31
  */
32 32
 	Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
  33
+
  34
+/**
  35
+ * Load all plugin routes.  See the CakePlugin documentation on 
  36
+ * how to customize the loading of plugin routes.
  37
+ */
  38
+	CakePlugin::routes();
  39
+
  40
+/**
  41
+ * Load the CakePHP default routes. Remove this if you do not want to use
  42
+ * the built-in default routes.
  43
+ */
  44
+	require CAKE . 'Config' . DS . 'routes.php';
3  app/webroot/index.php
@@ -76,5 +76,6 @@
76 76
 	}
77 77
 
78 78
 	App::uses('Dispatcher', 'Routing');
  79
+
79 80
 	$Dispatcher = new Dispatcher();
80  
-	$Dispatcher->dispatch(new CakeRequest());
  81
+	$Dispatcher->dispatch(new CakeRequest(), new CakeResponse(array('charset' => Configure::read('App.encoding'))));
83  lib/Cake/Config/routes.php
... ...
@@ -0,0 +1,83 @@
  1
+<?php
  2
+/**
  3
+ * Default routes that CakePHP provides as catch all routes.
  4
+ *
  5
+ * PHP 5
  6
+ *
  7
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8
+ * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9
+ *
  10
+ * Licensed under The MIT License
  11
+ * Redistributions of files must retain the above copyright notice.
  12
+ *
  13
+ * @copyright     Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14
+ * @link          http://cakephp.org CakePHP(tm) Project
  15
+ * @package       cake.config
  16
+ * @since         CakePHP(tm) v 2.0
  17
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  18
+ */
  19
+
  20
+/**
  21
+ * Connects the default, built-in routes, including prefix and plugin routes. The following routes are created
  22
+ * in the order below:
  23
+ *
  24
+ * For each of the Routing.prefixes the following routes are created. Routes containing `:plugin` are only
  25
+ * created when your application has one or more plugins.
  26
+ *
  27
+ * - `/:prefix/:plugin` a plugin shortcut route.
  28
+ * - `/:prefix/:plugin/:action/*` a plugin shortcut route.
  29
+ * - `/:prefix/:plugin/:controller`
  30
+ * - `/:prefix/:plugin/:controller/:action/*`
  31
+ * - `/:prefix/:controller`
  32
+ * - `/:prefix/:controller/:action/*`
  33
+ *
  34
+ * If plugins are found in your application the following routes are created:
  35
+ *
  36
+ * - `/:plugin` a plugin shortcut route.
  37
+ * - `/:plugin/:action/*` a plugin shortcut route.
  38
+ * - `/:plugin/:controller`
  39
+ * - `/:plugin/:controller/:action/*`
  40
+ *
  41
+ * And lastly the following catch-all routes are connected.
  42
+ *
  43
+ * - `/:controller'
  44
+ * - `/:controller/:action/*'
  45
+ *
  46
+ * You can disable the connection of default routes by deleting the require inside APP/Config/routes.php.
  47
+ */
  48
+	$prefixes = Router::prefixes();
  49
+
  50
+	if ($plugins = CakePlugin::loaded()) {
  51
+		App::uses('PluginShortRoute', 'Routing/Route');
  52
+		foreach ($plugins as $key => $value) {
  53
+			$plugins[$key] = Inflector::underscore($value);
  54
+		}
  55
+		$pluginPattern = implode('|', $plugins);
  56
+		$match = array('plugin' => $pluginPattern);
  57
+		$shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern);
  58
+
  59
+		foreach ($prefixes as $prefix) {
  60
+			$params = array('prefix' => $prefix, $prefix => true);
  61
+			$indexParams = $params + array('action' => 'index');
  62
+			Router::connect("/{$prefix}/:plugin", $indexParams, $shortParams);
  63
+			Router::connect("/{$prefix}/:plugin/:controller", $indexParams, $match);
  64
+			Router::connect("/{$prefix}/:plugin/:controller/:action/*", $params, $match);
  65
+		}
  66
+		Router::connect('/:plugin', array('action' => 'index'), $shortParams);
  67
+		Router::connect('/:plugin/:controller', array('action' => 'index'), $match);
  68
+		Router::connect('/:plugin/:controller/:action/*', array(), $match);
  69
+	}
  70
+
  71
+	foreach ($prefixes as $prefix) {
  72
+		$params = array('prefix' => $prefix, $prefix => true);
  73
+		$indexParams = $params + array('action' => 'index');
  74
+		Router::connect("/{$prefix}/:controller", $indexParams);
  75
+		Router::connect("/{$prefix}/:controller/:action/*", $params);
  76
+	}
  77
+	Router::connect('/:controller', array('action' => 'index'));
  78
+	Router::connect('/:controller/:action/*');
  79
+
  80
+	$namedConfig = Router::namedConfig();
  81
+	if ($namedConfig['rules'] === false) {
  82
+		Router::connectNamed(true);
  83
+	}
16  lib/Cake/Console/Templates/skel/Config/routes.php
... ...
@@ -1,6 +1,6 @@
1 1
 <?php
2 2
 /**
3  
- * Routes Configuration
  3
+ * Routes configuration
4 4
  *
5 5
  * In this file, you set up routes to your controllers and their actions.
6 6
  * Routes are very important mechanism that allows you to freely connect
@@ -20,15 +20,25 @@
20 20
  * @since         CakePHP(tm) v 0.2.9
21 21
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
22 22
  */
23  
-
24 23
 /**
25 24
  * Here, we are connecting '/' (base path) to controller called 'Pages',
26 25
  * its action called 'display', and we pass a param to select the view file
27 26
  * to use (in this case, /app/View/Pages/home.ctp)...
28 27
  */
29 28
 	Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
30  
-
31 29
 /**
32 30
  * ...and connect the rest of 'Pages' controller's urls.
33 31
  */
34 32
 	Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
  33
+
  34
+/**
  35
+ * Load all plugin routes.  See the CakePlugin documentation on 
  36
+ * how to customize the loading of plugin routes.
  37
+ */
  38
+	CakePlugin::routes();
  39
+
  40
+/**
  41
+ * Load the CakePHP default routes. Remove this if you do not want to use
  42
+ * the built-in default routes.
  43
+ */
  44
+	require CAKE . 'Config' . DS . 'routes.php';
2  lib/Cake/Console/Templates/skel/webroot/index.php
@@ -77,4 +77,4 @@
77 77
 
78 78
 	App::uses('Dispatcher', 'Routing');
79 79
 	$Dispatcher = new Dispatcher();
80  
-	$Dispatcher->dispatch(new CakeRequest());
  80
+	$Dispatcher->dispatch(new CakeRequest(), new CakeResponse(array('charset' => Configure::read('App.encoding'))));
4  lib/Cake/Controller/CakeErrorController.php
@@ -22,8 +22,8 @@ class CakeErrorController extends AppController {
22 22
  * @access public
23 23
  * @return void
24 24
  */
25  
-	public function __construct($request = null) {
26  
-		parent::__construct($request);
  25
+	public function __construct($request = null, $response = null) {
  26
+		parent::__construct($request, $response);
27 27
 		$this->constructClasses();
28 28
 		$this->Components->trigger('initialize', array(&$this));
29 29
 		$this->_set(array('cacheAction' => false, 'viewPath' => 'Errors'));
96  lib/Cake/Controller/Controller.php
@@ -291,10 +291,11 @@ class Controller extends Object {
291 291
 /**
292 292
  * Constructor.
293 293
  *
294  
- * @param CakeRequest $request Request object for this controller can be null for testing.
295  
- *  But expect that features that use the params will not work.
  294
+ * @param CakeRequest $request Request object for this controller. Can be null for testing,
  295
+ *  but expect that features that use the request parameters will not work.
  296
+ * @param CakeResponse $response Response object for this controller. 
296 297
  */
297  
-	public function __construct($request = null) {
  298
+	public function __construct($request = null, $response = null) {
298 299
 		if ($this->name === null) {
299 300
 			$this->name = substr(get_class($this), 0, strlen(get_class($this)) -10);
300 301
 		}
@@ -315,7 +316,9 @@ public function __construct($request = null) {
315 316
 		if ($request instanceof CakeRequest) {
316 317
 			$this->setRequest($request);
317 318
 		}
318  
-		$this->getResponse();
  319
+		if ($response instanceof CakeResponse) {
  320
+			$this->response = $response;
  321
+		}
319 322
 		parent::__construct();
320 323
 	}
321 324
 
@@ -440,6 +443,72 @@ public function setRequest(CakeRequest $request) {
440 443
 	}
441 444
 
442 445
 /**
  446
+ * Dispatches the controller action.  Checks that the action 
  447
+ * exists and isn't private.
  448
+ *
  449
+ * @param CakeRequest $request
  450
+ * @return The resulting response.
  451
+ */
  452
+	public function invokeAction(CakeRequest $request) {
  453
+		$reflection = new ReflectionClass($this);
  454
+		try {
  455
+			$method = $reflection->getMethod($request->params['action']);
  456
+
  457
+			if ($this->_isPrivateAction($method, $request)) {
  458
+				throw new PrivateActionException(array(
  459
+					'controller' => $this->name . "Controller",
  460
+					'action' => $request->params['action']
  461
+				));
  462
+			}
  463
+			return $method->invokeArgs($this, $request->params['pass']);
  464
+
  465
+		} catch (ReflectionException $e) {
  466
+			if ($this->scaffold !== false) {
  467
+				return $this->_getScaffold($request);
  468
+			}
  469
+			throw new MissingActionException(array(
  470
+				'controller' => $this->name . "Controller",
  471
+				'action' => $request->params['action']
  472
+			));
  473
+		}
  474
+	}
  475
+
  476
+/**
  477
+ * Check if the request's action is marked as private, with an underscore, 
  478
+ * or if the request is attempting to directly accessing a prefixed action.
  479
+ *
  480
+ * @param ReflectionMethod $method The method to be invoked.
  481
+ * @param CakeRequest $request The request to check.
  482
+ * @return boolean
  483
+ */
  484
+	protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request) {
  485
+		$privateAction = (
  486
+			$method->name[0] === '_' || 
  487
+			!$method->isPublic() ||
  488
+			!in_array($method->name,  $this->methods)
  489
+		);
  490
+		$prefixes = Router::prefixes();
  491
+
  492
+		if (!$privateAction && !empty($prefixes)) {
  493
+			if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) {
  494
+				list($prefix, $action) = explode('_', $request->params['action']);
  495
+				$privateAction = in_array($prefix, $prefixes);
  496
+			}
  497
+		}
  498
+		return $privateAction;
  499
+	}
  500
+
  501
+/**
  502
+ * Returns a scaffold object to use for dynamically scaffolded controllers.
  503
+ *
  504
+ * @param CakeRequest $request
  505
+ * @return Scaffold
  506
+ */
  507
+	protected function _getScaffold(CakeRequest $request) {
  508
+		return new Scaffold($this, $request);
  509
+	}
  510
+
  511
+/**
443 512
  * Merge components, helpers, and uses vars from Controller::$_mergeParent and PluginAppController.
444 513
  *
445 514
  * @return void
@@ -512,18 +581,6 @@ public function constructClasses() {
512 581
 	}
513 582
 
514 583
 /**
515  
- * Gets the response object for this controller.  Will construct the response if it has not already been built.
516  
- *
517  
- * @return CakeResponse
518  
- */
519  
-	public function getResponse() {
520  
-		if (empty($this->response)) {
521  
-			$this->response = new $this->_responseClass(array('charset' => Configure::read('App.encoding')));
522  
-		}
523  
-		return $this->response;
524  
-	}
525  
-
526  
-/**
527 584
  * Perform the startup process for this controller.
528 585
  * Fire the Components and Controller callbacks in the correct order.
529 586
  *
@@ -790,7 +847,7 @@ public function validateErrors() {
790 847
  *
791 848
  * @param string $view View to use for rendering
792 849
  * @param string $layout Layout to use
793  
- * @return string Full output string of view contents
  850
+ * @return CakeResponse A response object containing the rendered view.
794 851
  * @link http://book.cakephp.org/view/980/render
795 852
  */
796 853
 	public function render($view = null, $layout = null) {
@@ -828,7 +885,8 @@ public function render($view = null, $layout = null) {
828 885
 
829 886
 		$this->autoRender = false;
830 887
 		$this->View = $View;
831  
-		return $this->response->body($View->render($view, $layout));
  888
+		$this->response->body($View->render($view, $layout));
  889
+		return $this->response;
832 890
 	}
833 891
 
834 892
 /**
@@ -879,7 +937,7 @@ public function flash($message, $url, $pause = 1, $layout = 'flash') {
879 937
 		$this->set('message', $message);
880 938
 		$this->set('pause', $pause);
881 939
 		$this->set('page_title', $message);
882  
-		$this->response->body($this->render(false, $layout));
  940
+		$this->render(false, $layout);
883 941
 	}
884 942
 
885 943
 /**
2  lib/Cake/Controller/Scaffold.php
@@ -159,7 +159,7 @@ public function __construct(Controller $controller, CakeRequest $request) {
159 159
  */
160 160
 	protected function _output() {
161 161
 		$this->controller->afterFilter();
162  
-		$this->controller->getResponse()->send();
  162
+		$this->controller->response->send();
163 163
 	}
164 164
 
165 165
 /**
2  lib/Cake/Core/Object.php
@@ -87,7 +87,7 @@ public function requestAction($url, $extra = array()) {
87 87
 		}
88 88
 
89 89
 		$dispatcher = new Dispatcher();
90  
-		return $dispatcher->dispatch($request, $extra);
  90
+		return $dispatcher->dispatch($request, new CakeResponse(), $extra);
91 91
 	}
92 92
 
93 93
 /**
6  lib/Cake/Error/ExceptionRenderer.php
@@ -22,6 +22,7 @@
22 22
 
23 23
 App::uses('Sanitize', 'Utility');
24 24
 App::uses('Router', 'Routing');
  25
+App::uses('CakeResponse', 'Network');
25 26
 
26 27
 /**
27 28
  * Exception Renderer.
@@ -148,10 +149,11 @@ protected function _getController($exception) {
148 149
 		if (!$request = Router::getRequest(false)) {
149 150
 			$request = new CakeRequest();
150 151
 		}
  152
+		$response = new CakeResponse(array('charset' => Configure::read('App.encoding')));
151 153
 		try {
152  
-			$controller = new CakeErrorController($request);
  154
+			$controller = new CakeErrorController($request, $response);
153 155
 		} catch (Exception $e) {
154  
-			$controller = new Controller($request);
  156
+			$controller = new Controller($request, $response);
155 157
 			$controller->viewPath = 'Errors';
156 158
 		}
157 159
 		return $controller;
12  lib/Cake/Network/CakeResponse.php
@@ -654,4 +654,14 @@ public function compress() {
654 654
 	public function download($filename) {
655 655
 		$this->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
656 656
 	}
657  
-}
  657
+
  658
+/**
  659
+ * String conversion.  Fetches the response body as a string.
  660
+ * Does *not* send headers.
  661
+ *
  662
+ * @return string
  663
+ */
  664
+	public function __toString() {
  665
+		return (string)$this->_body;
  666
+	}
  667
+}
97  lib/Cake/Routing/Dispatcher.php
@@ -72,19 +72,20 @@ public function __construct($url = null, $base = false) {
72 72
  * If the controller is found, and the action is not found an exception will be thrown.
73 73
  *
74 74
  * @param CakeRequest $request Request object to dispatch.
  75
+ * @param CakeResponse $response Response object to put the results of the dispatch into.
75 76
  * @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params
76 77
  * @return boolean Success
77 78
  * @throws MissingControllerException, MissingActionException, PrivateActionException if any of those error states
78 79
  *    are encountered.
79 80
  */
80  
-	public function dispatch(CakeRequest $request, $additionalParams = array()) {
81  
-		if ($this->asset($request->url) || $this->cached($request->here)) {
  81
+	public function dispatch(CakeRequest $request, CakeResponse $response, $additionalParams = array()) {
  82
+		if ($this->asset($request->url, $response) || $this->cached($request->here)) {
82 83
 			return;
83 84
 		}
84 85
 
85 86
 		$request = $this->parseParams($request, $additionalParams);
86 87
 		Router::setRequestInfo($request);
87  
-		$controller = $this->_getController($request);
  88
+		$controller = $this->_getController($request, $response);
88 89
 
89 90
 		if (!($controller instanceof Controller)) {
90 91
 			throw new MissingControllerException(array(
@@ -92,35 +93,7 @@ public function dispatch(CakeRequest $request, $additionalParams = array()) {
92 93
 			));
93 94
 		}
94 95
 
95  
-		if ($this->_isPrivateAction($request)) {
96  
-			throw new PrivateActionException(array(
97  
-				'controller' => Inflector::camelize($request->params['controller']) . "Controller",
98  
-				'action' => $request->params['action']
99  
-			));
100  
-		}
101  
-
102  
-		return $this->_invoke($controller, $request);
103  
-	}
104  
-
105  
-/**
106  
- * Check if the request's action is marked as private, with an underscore, of if the request is attempting to
107  
- * directly accessing a prefixed action.
108  
- *
109  
- * @param CakeRequest $request The request to check
110  
- * @return boolean
111  
- */
112  
-	protected function _isPrivateAction($request) {
113  
-		$privateAction = $request->params['action'][0] === '_';
114  
-		$prefixes = Router::prefixes();
115  
-
116  
-		if (!$privateAction && !empty($prefixes)) {
117  
-			if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) {
118  
-				list($prefix, $action) = explode('_', $request->params['action']);
119  
-				$privateAction = in_array($prefix, $prefixes);
120  
-			}
121  
-		}
122  
-
123  
-		return $privateAction;
  96
+		return $this->_invoke($controller, $request, $response);
124 97
 	}
125 98
 
126 99
 /**
@@ -133,26 +106,19 @@ protected function _isPrivateAction($request) {
133 106
  * @return string Output as sent by controller
134 107
  * @throws MissingActionException when the action being called is missing.
135 108
  */
136  
-	protected function _invoke(Controller $controller, CakeRequest $request) {
  109
+	protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) {
137 110
 		$controller->constructClasses();
138 111
 		$controller->startupProcess();
139 112
 
140  
-		$methods = array_flip($controller->methods);
141  
-
142  
-		if (!isset($methods[$request->params['action']])) {
143  
-			if ($controller->scaffold !== false) {
144  
-				return new Scaffold($controller, $request);
145  
-			}
146  
-			throw new MissingActionException(array(
147  
-				'controller' => Inflector::camelize($request->params['controller']) . "Controller",
148  
-				'action' => $request->params['action']
149  
-			));
  113
+		$render = true;
  114
+		$result = $controller->invokeAction($request);
  115
+		if ($result instanceof CakeResponse) {
  116
+			$render = false;
  117
+			$response = $result;
150 118
 		}
151  
-		$result = call_user_func_array(array(&$controller, $request->params['action']), $request->params['pass']);
152  
-		$response = $controller->getResponse();
153  
-
154  
-		if ($controller->autoRender) {
155  
-			$controller->render();
  119
+		
  120
+		if ($render && $controller->autoRender) {
  121
+			$response = $controller->render();
156 122
 		} elseif ($response->body() === null) {
157 123
 			$response->body($result);
158 124
 		}
@@ -192,15 +158,16 @@ public function parseParams(CakeRequest $request, $additionalParams = array()) {
192 158
 /**
193 159
  * Get controller to use, either plugin controller or application controller
194 160
  *
195  
- * @param array $params Array of parameters
  161
+ * @param CakeRequest $request Request object
  162
+ * @param CakeResponse $response Response for the controller.
196 163
  * @return mixed name of controller if not loaded, or object if loaded
197 164
  */
198  
-	protected function _getController($request) {
  165
+	protected function _getController($request, $response) {
199 166
 		$ctrlClass = $this->_loadController($request);
200 167
 		if (!$ctrlClass) {
201 168
 			return false;
202 169
 		}
203  
-		return new $ctrlClass($request);
  170
+		return new $ctrlClass($request, $response);
204 171
 	}
205 172
 
206 173
 /**
@@ -237,7 +204,6 @@ protected function _loadController($request) {
237 204
  */
238 205
 	protected function _loadRoutes() {
239 206
 		include APP . 'Config' . DS . 'routes.php';
240  
-		CakePlugin::routes();
241 207
 	}
242 208
 
243 209
 /**
@@ -270,10 +236,11 @@ public function cached($path) {
270 236
 /**
271 237
  * Checks if a requested asset exists and sends it to the browser
272 238
  *
273  
- * @param $url string $url Requested URL
  239
+ * @param string $url Requested URL
  240
+ * @param CakeResponse $response The response object to put the file contents in.
274 241
  * @return boolean True on success if the asset file was found and sent
275 242
  */
276  
-	public function asset($url) {
  243
+	public function asset($url, CakeResponse $response) {
277 244
 		if (strpos($url, '..') !== false || strpos($url, '.') === false) {
278 245
 			return false;
279 246
 		}
@@ -286,12 +253,9 @@ public function asset($url) {
286 253
 			strpos($url, 'cjs/') === 0 ||
287 254
 			preg_match('#^/((theme/[^/]+)/cjs/)|(([^/]+)(?<!js)/cjs)/#i', $url)
288 255
 		);
289  
-		if (!$this->response) {
290  
-			$this->response = new CakeResponse();
291  
-		}
292 256
 		if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) {
293  
-			$this->response->statusCode(404);
294  
-			$this->response->send();
  257
+			$response->statusCode(404);
  258
+			$response->send();
295 259
 			return true;
296 260
 		} elseif ($isCss) {
297 261
 			include WWW_ROOT . DS . $filters['css'];
@@ -326,7 +290,7 @@ public function asset($url) {
326 290
 		}
327 291
 
328 292
 		if ($assetFile !== null) {
329  
-			$this->_deliverAsset($assetFile, $ext);
  293
+			$this->_deliverAsset($response, $assetFile, $ext);
330 294
 			return true;
331 295
 		}
332 296
 		return false;
@@ -335,23 +299,24 @@ public function asset($url) {
335 299
 /**
336 300
  * Sends an asset file to the client
337 301
  *
  302
+ * @param CakeResponse $response The response object to use.
338 303
  * @param string $assetFile Path to the asset file in the file system
339 304
  * @param string $ext The extension of the file to determine its mime type
340 305
  * @return void
341 306
  */
342  
-	protected function _deliverAsset($assetFile, $ext) {
  307
+	protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
343 308
 		ob_start();
344  
-		$compressionEnabled = Configure::read('Asset.compress') && $this->response->compress();
345  
-		if ($this->response->type($ext) == $ext) {
  309
+		$compressionEnabled = Configure::read('Asset.compress') && $response->compress();
  310
+		if ($response->type($ext) == $ext) {
346 311
 			$contentType = 'application/octet-stream';
347 312
 			$agent = env('HTTP_USER_AGENT');
348 313
 			if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
349 314
 				$contentType = 'application/octetstream';
350 315
 			}
351  
-			$this->response->type($contentType);
  316
+			$response->type($contentType);
352 317
 		}
353  
-		$this->response->cache(filemtime($assetFile));
354  
-		$this->response->send();
  318
+		$response->cache(filemtime($assetFile));
  319
+		$response->send();
355 320
 		ob_clean();
356 321
 		if ($ext === 'css' || $ext === 'js') {
357 322
 			include($assetFile);
109  lib/Cake/Routing/Router.php
@@ -146,21 +146,6 @@ class Router {
146 146
 	protected static $_requests = array();
147 147
 
148 148
 /**
149  
- * Keeps Router state to determine if default routes have already been connected
150  
- *
151  
- * @var boolean
152  
- * @access private
153  
- */
154  
-	protected static $_defaultsMapped = false;
155  
-
156  
-/**
157  
- * Keeps track of whether the connection of default routes is enabled or disabled.
158  
- *
159  
- * @var boolean
160  
- */
161  
-	protected static $_connectDefaults = true;
162  
-
163  
-/**
164 149
  * Initial state is popualated the first time reload() is called which is at the bottom
165 150
  * of this file.  This is a cheat as get_class_vars() returns the value of static vars even if they
166 151
  * have changed.
@@ -414,19 +399,6 @@ public static function namedConfig() {
414 399
 	}
415 400
 
416 401
 /**
417  
- * Tell router to connect or not connect the default routes.
418  
- *
419  
- * If default routes are disabled all automatic route generation will be disabled
420  
- * and you will need to manually configure all the routes you want.
421  
- *
422  
- * @param boolean $connect Set to true or false depending on whether you want or don't want default routes.
423  
- * @return void
424  
- */
425  
-	public static function defaults($connect = true) {
426  
-		self::$_connectDefaults = $connect;
427  
-	}
428  
-
429  
-/**
430 402
  * Creates REST resource routes for the given controller(s)
431 403
  *
432 404
  * ### Options:
@@ -479,15 +451,8 @@ public static function prefixes() {
479 451
  * @return array Parsed elements from URL
480 452
  */
481 453
 	public static function parse($url) {
482  
-		if (!self::$_defaultsMapped && self::$_connectDefaults) {
483  
-			self::__connectDefaultRoutes();
484  
-		}
485  
-		$out = array(
486  
-			'pass' => array(),
487  
-			'named' => array()
488  
-		);
489  
-
490 454
 		$ext = null;
  455
+		$out = array();
491 456
 
492 457
 		if ($url && strpos($url, '/') !== 0) {
493 458
 			$url = '/' . $url;
@@ -548,74 +513,6 @@ private static function __parseExtension($url) {
548 513
 	}
549 514
 
550 515
 /**
551  
- * Connects the default, built-in routes, including prefix and plugin routes. The following routes are created
552  
- * in the order below:
553  
- *
554  
- * For each of the Routing.prefixes the following routes are created. Routes containing `:plugin` are only
555  
- * created when your application has one or more plugins.
556  
- *
557  
- * - `/:prefix/:plugin` a plugin shortcut route.
558  
- * - `/:prefix/:plugin/:action/*` a plugin shortcut route.
559  
- * - `/:prefix/:plugin/:controller`
560  
- * - `/:prefix/:plugin/:controller/:action/*`
561  
- * - `/:prefix/:controller`
562  
- * - `/:prefix/:controller/:action/*`
563  
- *
564  
- * If plugins are found in your application the following routes are created:
565  
- *
566  
- * - `/:plugin` a plugin shortcut route.
567  
- * - `/:plugin/:action/*` a plugin shortcut route.
568  
- * - `/:plugin/:controller`
569  
- * - `/:plugin/:controller/:action/*`
570  
- *
571  
- * And lastly the following catch-all routes are connected.
572  
- *
573  
- * - `/:controller'
574  
- * - `/:controller/:action/*'
575  
- *
576  
- * You can disable the connection of default routes with Router::defaults().
577  
- *
578  
- * @return void
579  
- * @access private
580  
- */
581  
-	private static function __connectDefaultRoutes() {
582  
-		if ($plugins = CakePlugin::loaded()) {
583  
-			App::uses('PluginShortRoute', 'Routing/Route');
584  
-			foreach ($plugins as $key => $value) {
585  
-				$plugins[$key] = Inflector::underscore($value);
586  
-			}
587  
-			$pluginPattern = implode('|', $plugins);
588  
-			$match = array('plugin' => $pluginPattern);
589  
-			$shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern);
590  
-
591  
-			foreach (self::$_prefixes as $prefix) {
592  
-				$params = array('prefix' => $prefix, $prefix => true);
593  
-				$indexParams = $params + array('action' => 'index');
594  
-				self::connect("/{$prefix}/:plugin", $indexParams, $shortParams);
595  
-				self::connect("/{$prefix}/:plugin/:controller", $indexParams, $match);
596  
-				self::connect("/{$prefix}/:plugin/:controller/:action/*", $params, $match);
597  
-			}
598  
-			self::connect('/:plugin', array('action' => 'index'), $shortParams);
599  
-			self::connect('/:plugin/:controller', array('action' => 'index'), $match);
600  
-			self::connect('/:plugin/:controller/:action/*', array(), $match);
601  
-		}
602  
-
603  
-		foreach (self::$_prefixes as $prefix) {
604  
-			$params = array('prefix' => $prefix, $prefix => true);
605  
-			$indexParams = $params + array('action' => 'index');
606  
-			self::connect("/{$prefix}/:controller", $indexParams);
607  
-			self::connect("/{$prefix}/:controller/:action/*", $params);
608  
-		}
609  
-		self::connect('/:controller', array('action' => 'index'));
610  
-		self::connect('/:controller/:action/*');
611  
-
612  
-		if (self::$_namedConfig['rules'] === false) {
613  
-			self::connectNamed(true);
614  
-		}
615  
-		self::$_defaultsMapped = true;
616  
-	}
617  
-
618  
-/**
619 516
  * Takes parameter and path information back from the Dispatcher, sets these
620 517
  * parameters as the current request parameters that are merged with url arrays 
621 518
  * created later in the request.
@@ -1024,8 +921,8 @@ public static function reverse($params, $full = false) {
1024 921
 		} else {
1025 922
 			$url = $params['url'];
1026 923
 		}
1027  
-		$pass = $params['pass'];
1028  
-		$named = $params['named'];
  924
+		$pass = isset($params['pass']) ? $params['pass'] : array();
  925
+		$named = isset($params['named']) ? $params['named'] : array();
1029 926
 
1030 927
 		unset(
1031 928
 			$params['pass'], $params['named'], $params['paging'], $params['models'], $params['url'], $url['url'],
6  lib/Cake/Test/Case/Console/Command/ApiShellTest.php
@@ -62,9 +62,9 @@ public function testMethodNameDetection () {
62 62
 			'5. constructClasses()',
63 63
 			'6. disableCache()',
64 64
 			'7. flash($message, $url, $pause = 1, $layout = \'flash\')',
65  
-			'8. getResponse()',
66  
-			'9. header($status)',
67  
-			'10. httpCodes($code = NULL)',
  65
+			'8. header($status)',
  66
+			'9. httpCodes($code = NULL)',
  67
+			'10. invokeAction($request)',
68 68
 			'11. loadModel($modelClass = NULL, $id = NULL)',
69 69
 			'12. paginate($object = NULL, $scope = array (), $whitelist = array ())',
70 70
 			'13. postConditions($data = array (), $op = NULL, $bool = \'AND\', $exclusive = false)',
17  lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php
@@ -122,12 +122,12 @@ class AuthTestController extends Controller {
122 122
  * @access private
123 123
  * @return void
124 124
  */
125  
-	function __construct($request) {
  125
+	function __construct($request, $response) {
126 126
 		$request->addParams(Router::parse('/auth_test'));
127 127
 		$request->here = '/auth_test';
128 128
 		$request->webroot = '/';
129 129
 		Router::setRequestInfo($request);
130  
-		parent::__construct($request);
  130
+		parent::__construct($request, $response);
131 131
 	}
132 132
 
133 133
 /**
@@ -292,12 +292,12 @@ public function redirect($url, $status = null, $exit = true) {
292 292
 }
293 293
 
294 294
 /**
295  
-* AuthTest class
  295
+* AuthComponentTest class
296 296
 *
297 297
 * @package       cake
298 298
 * @package       cake.tests.cases.libs.controller.components
299 299
 */
300  
-class AuthTest extends CakeTestCase {
  300
+class AuthComponentTest extends CakeTestCase {
301 301
 
302 302
 /**
303 303
  * name property
@@ -339,7 +339,7 @@ public function setUp() {
339 339
 
340 340
 		$request = new CakeRequest(null, false);
341 341
 
342  
-		$this->Controller = new AuthTestController($request);
  342
+		$this->Controller = new AuthTestController($request, $this->getMock('CakeResponse'));
343 343
 
344 344
 		$collection = new ComponentCollection();
345 345
 		$collection->init($this->Controller);
@@ -351,6 +351,7 @@ public function setUp() {
351 351
 
352 352
 		$this->initialized = true;
353 353
 		Router::reload();
  354
+		Router::connect('/:controller/:action/*');
354 355
 
355 356
 		$User = ClassRegistry::init('AuthUser');
356 357
 		$User->updateAll(array('password' => $User->getDataSource()->value(Security::hash('cake', null, true))));
@@ -925,6 +926,7 @@ public function testAdminRoute() {
925 926
 		$prefixes = Configure::read('Routing.prefixes');
926 927
 		Configure::write('Routing.prefixes', array('admin'));
927 928
 		Router::reload();
  929
+		require CAKE . 'Config' . DS . 'routes.php';
928 930
 
929 931
 		$url = '/admin/auth_test/add';
930 932
 		$this->Auth->request->addParams(Router::parse($url));
@@ -960,7 +962,7 @@ public function testAjaxLogin() {
960 962
 
961 963
 		ob_start();
962 964
 		$Dispatcher = new Dispatcher();
963  
-		$Dispatcher->dispatch(new CakeRequest('/ajax_auth/add'), array('return' => 1));
  965
+		$Dispatcher->dispatch(new CakeRequest('/ajax_auth/add'), new CakeResponse(), array('return' => 1));
964 966
 		$result = ob_get_clean();
965 967
 
966 968
 		$this->assertEqual("Ajax!\nthis is the test element", str_replace("\r\n", "\n", $result));
@@ -977,6 +979,7 @@ public function testLoginActionRedirect() {
977 979
 		$admin = Configure::read('Routing.prefixes');
978 980
 		Configure::write('Routing.prefixes', array('admin'));
979 981
 		Router::reload();
  982
+		require CAKE . 'Config' . DS . 'routes.php';
980 983
 
981 984
 		$url = '/admin/auth_test/login';
982 985
 		$this->Auth->request->addParams(Router::parse($url));
@@ -1024,7 +1027,7 @@ public function testShutDown() {
1024 1027
  */
1025 1028
 	public function testComponentSettings() {
1026 1029
 		$request = new CakeRequest(null, false);
1027  
-		$this->Controller = new AuthTestController($request);
  1030
+		$this->Controller = new AuthTestController($request, $this->getMock('CakeResponse'));
1028 1031
 
1029 1032
 		$this->Controller->components = array(
1030 1033
 			'Auth' => array(
24  lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php
@@ -77,30 +77,6 @@ public function ajax2_layout() {
77 77
 	}
78 78
 }
79 79
 
80  
-/**
81  
- * RequestHandlerTestDisabledController class
82  
- *
83  
- * @package       cake.tests.cases.libs.controller.components
84  
- */
85  
-class RequestHandlerTestDisabledController extends Controller {
86  
-
87  
-/**
88  
- * uses property
89  
- *
90  
- * @var mixed null
91  
- * @access public
92  
- */
93  
-	public $uses = null;
94  
-
95  
-/**
96  
- * beforeFilter method
97  
- *
98  
- * @return void
99  
- */
100  
-	public function beforeFilter() {
101  
-		$this->RequestHandler->enabled = false;
102  
-	}
103  
-}
104 80
 
105 81
 /**
106 82
  * RequestHandlerComponentTest class
176  lib/Cake/Test/Case/Controller/ControllerTest.php
@@ -253,7 +253,7 @@ class TestController extends ControllerTestAppController {
253 253
  * @var string 'Name'
254 254
  * @access public
255 255
  */
256  
-	public $name = 'TestController';
  256
+	public $name = 'Test';
257 257
 
258 258
 /**
259 259
  * helpers property
@@ -261,7 +261,7 @@ class TestController extends ControllerTestAppController {
261 261
  * @var array
262 262
  * @access public
263 263
  */
264  
-	public $helpers = array('Session', 'Xml');
  264
+	public $helpers = array('Session');
265 265
 
266 266
 /**
267 267
  * components property
@@ -295,6 +295,26 @@ public function index($testId, $test2Id) {
295 295
 			'test2Id' => $test2Id
296 296
 		);
297 297
 	}
  298
+
  299
+	public function returner() {
  300
+		return 'I am from the controller.';
  301
+	}
  302
+	
  303
+	protected function protected_m() {
  304
+	
  305
+	}
  306
+
  307
+	private function private_m() {
  308
+	
  309
+	}
  310
+
  311
+	public function _hidden() {
  312
+	
  313
+	}
  314
+
  315
+	public function admin_add() {
  316
+	
  317
+	}
298 318
 }
299 319
 
300 320
 /**
@@ -303,7 +323,6 @@ public function index($testId, $test2Id) {
303 323
  * @package       cake.tests.cases.libs.controller
304 324
  */
305 325
 class TestComponent extends Object {
306  
-
307 326
 /**
308 327
  * beforeRedirect method
309 328
  *
@@ -418,7 +437,8 @@ public function teardown() {
418 437
  */
419 438
 	public function testLoadModel() {
420 439
 		$request = new CakeRequest('controller_posts/index');
421  
-		$Controller = new Controller($request);
  440
+		$response = $this->getMock('CakeResponse');
  441
+		$Controller = new Controller($request, $response);
422 442
 
423 443
 		$this->assertFalse(isset($Controller->ControllerPost));
424 444
 
@@ -524,8 +544,7 @@ public function testFlash() {
524 544
 		$request->webroot = '/';
525 545
 		$request->base = '/';
526 546
 
527  
-		$Controller = new Controller($request);
528  
-		$Controller->response = $this->getMock('CakeResponse', array('_sendHeader'));
  547
+		$Controller = new Controller($request, $this->getMock('CakeResponse', array('_sendHeader')));
529 548
 		$Controller->flash('this should work', '/flash');
530 549
 		$result = $Controller->response->body();
531 550
 
@@ -617,21 +636,21 @@ public function testRender() {
617 636
 		$request = new CakeRequest('controller_posts/index');
618 637
 		$request->params['action'] = 'index';
619 638
 
620  
-		$Controller = new Controller($request, $this->getMock('CakeResponse'));
  639
+		$Controller = new Controller($request, new CakeResponse());
621 640
 		$Controller->viewPath = 'Posts';
622 641
 
623 642
 		$result = $Controller->render('index');
624  
-		$this->assertPattern('/posts index/', $result);
  643
+		$this->assertPattern('/posts index/', (string)$result);
625 644
 
626 645
 		$Controller->view = 'index';
627 646
 		$result = $Controller->render();
628  
-		$this->assertPattern('/posts index/', $result);
  647
+		$this->assertPattern('/posts index/', (string)$result);
629 648
 
630 649
 		$result = $Controller->render('/Elements/test_element');
631  
-		$this->assertPattern('/this is the test element/', $result);
  650
+		$this->assertPattern('/this is the test element/', (string)$result);
632 651
 		$Controller->view = null;
633 652
 
634  
-		$Controller = new TestController($request);
  653
+		$Controller = new TestController($request, new CakeResponse());
635 654
 		$Controller->helpers = array('Html');
636 655
 		$Controller->constructClasses();
637 656
 		$Controller->ControllerComment->validationErrors = array('title' => 'tooShort');
@@ -668,7 +687,7 @@ public function testComponentBeforeRenderChangingViewClass() {
668 687
 				CAKE . 'Test' . DS . 'test_app' . DS . 'View'. DS
669 688
 			)
670 689
 		), true);
671  
-		$Controller = new Controller($this->getMock('CakeRequest'));
  690
+		$Controller = new Controller($this->getMock('CakeRequest'), new CakeResponse());
672 691
 		$Controller->uses = array();
673 692
 		$Controller->components = array('Test');
674 693
 		$Controller->constructClasses();
@@ -676,7 +695,7 @@ public function testComponentBeforeRenderChangingViewClass() {
676 695
 		$Controller->viewPath = 'Posts';
677 696
 		$Controller->theme = 'TestTheme';
678 697
 		$result = $Controller->render('index');
679  
-		$this->assertPattern('/default test_theme layout/', $result);
  698
+		$this->assertPattern('/default test_theme layout/', (string)$result);
680 699
 		App::build();
681 700
 	}
682 701
 
@@ -689,7 +708,7 @@ public function testComponentBeforeRenderChangingViewClass() {
689 708
 	public function testToBeInheritedGuardmethods() {
690 709
 		$request = new CakeRequest('controller_posts/index');
691 710
 
692  
-		$Controller = new Controller($request);
  711
+		$Controller = new Controller($request, $this->getMock('CakeResponse'));
693 712
 		$this->assertTrue($Controller->_beforeScaffold(''));
694 713
 		$this->assertTrue($Controller->_afterScaffoldSave(''));
695 714
 		$this->assertTrue($Controller->_afterScaffoldSaveError(''));
@@ -1119,7 +1138,7 @@ public function testRequestHandlerPrefers(){
1119 1138
 
1120 1139
 		$request = new CakeRequest('controller_posts/index');
1121 1140
 
1122  
-		$Controller = new Controller($request);
  1141
+		$Controller = new Controller($request, $this->getMock('CakeResponse'));
1123 1142
 
1124 1143
 		$Controller->components = array("RequestHandler");
1125 1144
 		$Controller->modelClass='ControllerPost';
@@ -1140,8 +1159,8 @@ public function testRequestHandlerPrefers(){
1140 1159
  * @return void
1141 1160
  */
1142 1161
 	public function testControllerHttpCodes() {
1143  
-		$Controller = new Controller(null);
1144  
-		$Controller->response = $this->getMock('CakeResponse', array('httpCodes'));
  1162
+		$response = $this->getMock('CakeResponse', array('httpCodes'));
  1163
+		$Controller = new Controller(null, $response);
1145 1164
 		$Controller->response->expects($this->at(0))->method('httpCodes')->with(null);
1146 1165
 		$Controller->response->expects($this->at(1))->method('httpCodes')->with(100);
1147 1166
 		$Controller->httpCodes();
@@ -1237,8 +1256,9 @@ public function testPropertyCompatibilityAndModelsComponents() {
1237 1256
 	public function testPaginateBackwardsCompatibility() {
1238 1257
 		$request = new CakeRequest('controller_posts/index');
1239 1258
 		$request->params['pass'] = $request->params['named'] = array();
  1259
+		$response = $this->getMock('CakeResponse', array('httpCodes'));
1240 1260
 
1241  
-		$Controller = new Controller($request);
  1261
+		$Controller = new Controller($request, $response);
1242 1262
 		$Controller->uses = array('ControllerPost', 'ControllerComment');
1243 1263
 		$Controller->passedArgs[] = '1';
1244 1264
 		$Controller->params['url'] = array();
@@ -1258,4 +1278,124 @@ public function testPaginateBackwardsCompatibility() {
1258 1278
 		$this->assertIdentical($Controller->params['paging']['ControllerPost']['prevPage'], false);
1259 1279
 		$this->assertIdentical($Controller->params['paging']['ControllerPost']['nextPage'], true);
1260 1280
 	}
  1281
+
  1282
+/**
  1283
+ * testMissingAction method
  1284
+ *
  1285
+ * @expectedException MissingActionException
  1286
+ * @expectedExceptionMessage Action TestController::missing() could not be found.
  1287
+ * @return void
  1288
+ */
  1289
+	public function testInvokeActionMissingAction() {
  1290
+		$url = new CakeRequest('test/missing');
  1291
+		$url->addParams(array('controller' => 'test_controller', 'action' => 'missing'));
  1292
+		$response = $this->getMock('CakeResponse');
  1293
+
  1294
+		$Controller = new TestController($url, $response);
  1295
+		$Controller->invokeAction($url);
  1296
+	}
  1297
+
  1298
+/**
  1299
+ * test invoking private methods.
  1300
+ *
  1301
+ * @expectedException PrivateActionException
  1302
+ * @expectedExceptionMessage Private Action TestController::private_m() is not directly accessible.
  1303
+ * @return void
  1304
+ */
  1305
+	public function testInvokeActionPrivate() {
  1306
+		$url = new CakeRequest('test/private_m/');
  1307
+		$url->addParams(array('controller' => 'test_controller', 'action' => 'private_m'));
  1308
+		$response = $this->getMock('CakeResponse');
  1309
+
  1310
+		$Controller = new TestController($url, $response);
  1311
+		$Controller->invokeAction($url);
  1312
+	}
  1313
+
  1314
+/**