Moving private method detection into Controller.

This fixes an issue where potected methods would
not be called, and no exception would be raised.
This commit is contained in:
mark_story 2011-07-09 13:56:51 -04:00 committed by mark_story
parent 8bfc0a859d
commit 177cd39abb
3 changed files with 62 additions and 50 deletions

View file

@ -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. * Merge components, helpers, and uses vars from Controller::$_mergeParent and PluginAppController.
* *

View file

@ -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); 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. * 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. * 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->constructClasses();
$controller->startupProcess(); $controller->startupProcess();
$methods = array_flip($controller->methods); $result = $controller->invokeAction($request);
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']);
if ($result instanceof CakeResponse) { if ($result instanceof CakeResponse) {
$response = $result; $response = $result;
} }
if ($controller->autoRender) { if ($controller->autoRender) {
$response = $controller->render(); $response = $controller->render();
} elseif ($response->body() === null) { } elseif ($response->body() === null) {

View file

@ -50,11 +50,7 @@ class TestDispatcher extends Dispatcher {
* @return void * @return void
*/ */
protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) { protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) {
if ($result = parent::_invoke($controller, $request, $response)) { $result = parent::_invoke($controller, $request, $response);
if ($result[0] === 'missingAction') {
return $result;
}
}
return $controller; 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 * @expectedException PrivateActionException
* @expectedExceptionMessage Action SomePagesController::redirect() could not be found. * @expectedExceptionMessage Private Action SomePagesController::redirect() is not directly accessible.
* @return void * @return void
*/ */
public function testMissingActionFromBaseClassMethods() { public function testPrivateActionFromBaseClassMethods() {
$Dispatcher = new TestDispatcher(); $Dispatcher = new TestDispatcher();
Configure::write('App.baseUrl','/index.php'); Configure::write('App.baseUrl','/index.php');
$url = new CakeRequest('some_pages/redirect/param:value/param2:value2'); $url = new CakeRequest('some_pages/redirect/param:value/param2:value2');