8000 Add better error message when controller action isn't callable · symfony/symfony@23b81a8 · 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 23b81a8

Browse files
committed
Add better error message when controller action isn't callable
1 parent a469c56 commit 23b81a8

File tree

3 files changed

+85
-13
lines changed

3 files changed

+85
-13
lines changed

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

Lines changed: 4 additions & 6 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,10 +111,10 @@ 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.'
119-
)
116+
'The controller for URI "/" is not callable. Expected method "bar" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"',
117+
),
120118
);
121119
}
122120

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ 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+
$message = sprintf('The controller for URI "%s" is not callable. ', $request->getPathInfo());
84+
85+
$message .= $this->getControllerError($callable);
86+
87+
throw new \InvalidArgumentException($message);
8488
}
8589

8690
return $callable;
@@ -155,4 +159,52 @@ protected function createController($controller)
155159

156160
return array(new $class(), $method);
157161
}
162+
163+
private function getControllerError(array $callable)
164+
{
165+
list($controller, $method) = $callable;
166+
167+
if (is_string($controller) && !class_exists($controller)) {
168+
return sprintf('Class "%s" does not exist.', $controller);
169+
}
170+
171+
$reflection = new \ReflectionClass($controller);
172+
173+
if (!method_exists($controller, $method)) {
174+
$collection = array_map(
175+
function (\ReflectionMethod $method) {
176+
return $method->getName();
177+
},
178+
$reflection->getMethods(\ReflectionMethod::IS_PUBLIC)
179+
);
180+
181+
$alternatives = array();
182+
183+
foreach ($collection as $item) {
184+
$lev = levenshtein($method, $item);
185+
186+
if ($lev <= strlen($method) / 3 || false !== strpos($item, $method)) {
187+
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
188+
}
189+
}
190+
191+
asort($alternatives);
192+
193+
$message = sprintf('Expected method "%s" on class "%s"', $method, $reflection->getName());
194+
195+
if (count($alternatives) > 0) {
196+
$message .= sprintf(', did you mean "%s"?', implode('", "', array_keys($alternatives)));
197+
} else {
198+
$message .= sprintf('. Available methods: "%s"', implode('", "', $collection));
199+
}
200+
201+
return $message;
202+
}
203+
A3E2
204+
return sprintf(
205+
'Method "%s" on class "%s" should be public and non-abstract',
206+
$method,
207+
$reflection->getName()
208+
);
209+
}
158210
}

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

@@ -240,3 +243,22 @@ protected function controllerMethod5(Request $request)
240243
function some_controller_function($foo, $foobar)
241244
{
242245
}
246+
247+
class ControllerTest
248+
{
249+
public function publicAction()
250+
{
251+
}
252+
253+
private function privateAction()
254+
{
255+
}
256+
257+
protected function protectedAction()
258+
{
259+
}
260+
261+
public static function staticAction()
262+
{
263+
}
264+
}

0 commit comments

Comments
 (0)
0