diff --git a/lib/Cake/Controller/Controller.php b/lib/Cake/Controller/Controller.php index 8f68bf91c..c15548a17 100644 --- a/lib/Cake/Controller/Controller.php +++ b/lib/Cake/Controller/Controller.php @@ -442,6 +442,61 @@ class Controller extends Object { } } +/** + * Dispatches the controller action. Checks that the action + * exists and isn't private. + * + * @param CakeRequest $request + * @return The resulting response. + */ + public function invokeAction(CakeRequest $request) { + $reflection = new ReflectionClass($this); + try { + $method = $reflection->getMethod($request->params['action']); + + if ($this->_isPrivateAction($method, $request)) { + throw new PrivateActionException(array( + 'controller' => $this->name . "Controller", + 'action' => $request->params['action'] + )); + } + return $method->invokeArgs($this, $request->params['pass']); + + } catch (ReflectionException $e) { + if ($this->scaffold !== false) { + return new Scaffold($this, $request); + } + throw new MissingActionException(array( + 'controller' => $this->name . "Controller", + 'action' => $request->params['action'] + )); + } + } + +/** + * Check if the request's action is marked as private, with an underscore, of if the request is attempting to + * directly accessing a prefixed action. + * + * @param ReflectionMethod $method The method to be invoked. + * @param CakeRequest $request The request to check. + * @return boolean + */ + protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request) { + $privateAction = ( + $method->name[0] === '_' || + !$method->isPublic() || + !in_array($method->name, $this->methods) + ); + $prefixes = Router::prefixes(); + + if (!$privateAction && !empty($prefixes)) { + if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) { + list($prefix, $action) = explode('_', $request->params['action']); + $privateAction = in_array($prefix, $prefixes); + } + } + return $privateAction; + } /** * Merge components, helpers, and uses vars from Controller::$_mergeParent and PluginAppController. * diff --git a/lib/Cake/Routing/Dispatcher.php b/lib/Cake/Routing/Dispatcher.php index 99791e7b4..90a70a039 100644 --- a/lib/Cake/Routing/Dispatcher.php +++ b/lib/Cake/Routing/Dispatcher.php @@ -93,37 +93,9 @@ class Dispatcher { )); } - if ($this->_isPrivateAction($request)) { - throw new PrivateActionException(array( - 'controller' => Inflector::camelize($request->params['controller']) . "Controller", - 'action' => $request->params['action'] - )); - } - return $this->_invoke($controller, $request, $response); } -/** - * Check if the request's action is marked as private, with an underscore, of if the request is attempting to - * directly accessing a prefixed action. - * - * @param CakeRequest $request The request to check - * @return boolean - */ - protected function _isPrivateAction($request) { - $privateAction = $request->params['action'][0] === '_'; - $prefixes = Router::prefixes(); - - if (!$privateAction && !empty($prefixes)) { - if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) { - list($prefix, $action) = explode('_', $request->params['action']); - $privateAction = in_array($prefix, $prefixes); - } - } - - return $privateAction; - } - /** * Initializes the components and models a controller will be using. * Triggers the controller action, and invokes the rendering if Controller::$autoRender is true and echo's the output. @@ -138,22 +110,11 @@ class Dispatcher { $controller->constructClasses(); $controller->startupProcess(); - $methods = array_flip($controller->methods); - - if (!isset($methods[$request->params['action']])) { - if ($controller->scaffold !== false) { - return new Scaffold($controller, $request); - } - throw new MissingActionException(array( - 'controller' => Inflector::camelize($request->params['controller']) . "Controller", - 'action' => $request->params['action'] - )); - } - $result = call_user_func_array(array(&$controller, $request->params['action']), $request->params['pass']); - + $result = $controller->invokeAction($request); if ($result instanceof CakeResponse) { $response = $result; } + if ($controller->autoRender) { $response = $controller->render(); } elseif ($response->body() === null) { diff --git a/lib/Cake/Test/Case/Routing/DispatcherTest.php b/lib/Cake/Test/Case/Routing/DispatcherTest.php index a7c7ae527..46de1d30a 100644 --- a/lib/Cake/Test/Case/Routing/DispatcherTest.php +++ b/lib/Cake/Test/Case/Routing/DispatcherTest.php @@ -50,11 +50,7 @@ class TestDispatcher extends Dispatcher { * @return void */ protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) { - if ($result = parent::_invoke($controller, $request, $response)) { - if ($result[0] === 'missingAction') { - return $result; - } - } + $result = parent::_invoke($controller, $request, $response); return $controller; } @@ -733,13 +729,13 @@ class DispatcherTest extends CakeTestCase { } /** - * test that methods declared in Controller are treated as missing methods. + * test that methods declared in Controller are treated as private methods. * - * @expectedException MissingActionException - * @expectedExceptionMessage Action SomePagesController::redirect() could not be found. + * @expectedException PrivateActionException + * @expectedExceptionMessage Private Action SomePagesController::redirect() is not directly accessible. * @return void */ - public function testMissingActionFromBaseClassMethods() { + public function testPrivateActionFromBaseClassMethods() { $Dispatcher = new TestDispatcher(); Configure::write('App.baseUrl','/index.php'); $url = new CakeRequest('some_pages/redirect/param:value/param2:value2');