8000 Add better error message when controller action isn't callable · symfony/symfony@33e4482 · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 33e4482

Browse files
committed
Add better error message when controller action isn't callable
1 parent 98b714b commit 33e4482

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ public function testGetControllerInvokableService()
101101
*/
102102
public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null)
103103
{
104-
$this->setExpectedException($exceptionName, $exceptionMessage);
105-
106-
parent::testGetControllerOnNonUndefinedFunction($controller);
104+
parent::testGetControllerOnNonUndefinedFunction($controller, $exceptionName, $exceptionMessage);
107105
}
108106

109107
public function getUndefinedControllers()
@@ -113,9 +111,9 @@ public function getUndefinedControllers()
113111
array('foo::bar', '\InvalidArgumentException', 'Class "foo" does not exist.'),
114112
array('stdClass', '\LogicException', 'Unable to parse the controller name "stdClass".'),
115113
array(
116-
'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar',
114+
'Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::bar',
117115
'\InvalidArgumentException',
118-
'Controller "Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar" for URI "/" is not callable.',
116+
'The controller for URI "/" is not callable. Expected method "bar" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"',
119117
),
120118
);
121119
}

src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function getController(Request $request)
8080
$callable = $this->createController($controller);
8181

8282
if (!is_callable($callable)) {
83-
throw new \InvalidArgumentException(sprintf('Controller "%s" for URI "%s" is not callable.', $controller, $request->getPathInfo()));
83+
throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s', $request->getPathInfo(), $this->getControllerError($callable)));
8484
}
8585

8686
return $callable;
@@ -167,4 +167,61 @@ protected function instantiateController($class)
167167
{
168168
return new $class();
169169
}
170+
171+
private function getControllerError($callable)
172+
{
173+
if (is_string($callable)) {
174+
if (!function_exists($callable)) {
175+
return sprintf('Function "%s" does not exist.', $callable);
176+
}
177+
178+
if (false !== strpos($callable, '::')) {
179+
$callable = explode('::', $callable);
180+
}
181+
}
182+
183+
if (!is_array($callable)) {
184+
return sprintf('Invalid type for controller given, expected string or array, got "%s".', gettype($callable));
185+
}
186+
187+
if (2 !== count($callable)) {
188+
return sprintf('Invalid format for controller, expected array(controller, method) or controller::method.');
189+
}
190+
191+
list($controller, $method) = $callable;
192+
193+
if (is_string($controller) && !class_exists($controller)) {
194+
return sprintf('Class "%s" does not exist.', $controller);
195+
}
196+
197+
$reflection = new \ReflectionClass($controller);
198+
199+
if (method_exists($controller, $method)) {
200+
return sprintf('Method "%s" on class "%s" should be public and non-abstract.', $method, $reflection->getName());
201+
}
202+
203+
$collection = get_class_methods($controller);
204+
205+
$alternatives = array();
206+
207+
foreach ($collection as $item) {
208+
$lev = levenshtein($method, $item);
209+
210+
if ($lev <= strlen($method) / 3 || false !== strpos($item, $method)) {
211+
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
212+
}
213+
}
214+
215+
asort($alternatives);
216+
217+
$message = sprintf('Expected method "%s" on class "%s"', $method, $reflection->getName());
218+
219+
if (count($alternatives) > 0) {
220+
$message .= sprintf(', did you mean "%s"?', implode('", "', array_keys($alternatives)));
221+
} else {
222+
$message .= sprintf('. Available methods: "%s".', implode('", "', $collection));
223+
}
224+
225+
return $message;
226+
}
170227
}

src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ public function testGetControllerWithFunction()
111111

112112
/**
113113
* @dataProvider getUndefinedControllers
114-
* @expectedException \InvalidArgumentException
115114
*/
116-
public function testGetControllerOnNonUndefinedFunction($controller)
115+
public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null)
117116
{
118117
$resolver = $this->createControllerResolver();
118+
$this->setExpectedException($exceptionName, $exceptionMessage);
119119

120120
$request = Request::create('/');
121121
$request->attributes->set('_controller', $controller);
@@ -125,10 +125,13 @@ public function testGetControllerOnNonUndefinedFunction($controller)
125125
public function getUndefinedControllers()
126126
{
127127
return array(
128-
array('foo'),
129-
array('foo::bar'),
130-
array('stdClass'),
131-
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar'),
128+
array('foo', 'InvalidArgumentException', 'Unable to find controller "foo".'),
129+
array('foo::bar', 'InvalidArgumentException', 'Class "foo" does not exist.'),
130+
array('stdClass', 'InvalidArgumentException', 'Unable to find controller "stdClass".'),
131+
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'),
132+
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'),
133+
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'),
134+
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'),
132135
);
133136
}
134137

@@ -236,3 +239,22 @@ protected function controllerMethod5(Request $request)
236239
function some_controller_function($foo, $foobar)
237240
{
238241
}
242+
243+
class ControllerTest
244+
{
245+
public function publicAction()
246+
{
247+
}
248+
249+
private function privateAction()
250+
{
251+
}
252+
253+
protected function protectedAction()
254+
{
255+
}
256+
257+
public static function staticAction()
258+
{
259+
}
260+
}

0 commit comments

Comments
 (0)
0