Skip to content

Commit

Permalink
Moving private method detection into Controller.
Browse files Browse the repository at this point in the history
This fixes an issue where potected methods would
not be called, and no exception would be raised.
  • Loading branch information
markstory committed Jul 23, 2011
1 parent 8bfc0a8 commit 177cd39
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 50 deletions.
55 changes: 55 additions & 0 deletions lib/Cake/Controller/Controller.php
Expand Up @@ -442,6 +442,61 @@ public function setRequest(CakeRequest $request) {
}
}

/**
* 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.
*
Expand Down
43 changes: 2 additions & 41 deletions lib/Cake/Routing/Dispatcher.php
Expand Up @@ -93,37 +93,9 @@ public function dispatch(CakeRequest $request, CakeResponse $response, $addition
));
}

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.
Expand All @@ -138,22 +110,11 @@ protected function _invoke(Controller $controller, CakeRequest $request, CakeRes
$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) {
Expand Down
14 changes: 5 additions & 9 deletions lib/Cake/Test/Case/Routing/DispatcherTest.php
Expand Up @@ -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;
}

Expand Down Expand Up @@ -733,13 +729,13 @@ public function testMissingAction() {
}

/**
* 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');
Expand Down

0 comments on commit 177cd39

Please sign in to comment.