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.
*

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);
}
/**
* 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) {

View file

@ -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');