Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.6k
[HttpKernel] Add better error message when controller action isn't callable#10788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
array('Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest::staticsAction', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest", did you mean "staticAction"?'), | ||
array('Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest::privateAction', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest" should not be private'), | ||
array('Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest::protectedAction', 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest" should not be protected'), | ||
array('Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest::undefinedAction', 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Fixtures\ControllerTest". Available methods: "publicAction", "staticAction"'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
if the controller test class is used only in this file then it would be in the same file right?
Please add the DX label for this PR. |
$message .= sprintf(', did you mean "%s"?', implode('", "', array_keys($alternatives))); | ||
} else { | ||
$message .= sprintf('. Available methods: "%s"', implode('", "', $collection)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
In my opinion, the alternatives should be full_controller
strings.
So, don't just say, "Did you mean newAction"? If possible, I think we should say, did you mean "AcmeDemoBundle:Default:new"?
This would need to live actually on theControllerResolver::createController
method inside of the FrameworkBundle (as theAcmeDemoBundle:Default:new
syntax is specific to the framework).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The first part of the message isExpected method "%s" on class "%s"
. So saying something likeExpected method "newAction" on class "DefaultController", did you mean AcmeDemoBundle:Default:news?
doesn't feel right.
This checks for mistakes in the action name, if E.G you have a newsAction, but reference it just as new, then this message will give you an idea of the correct method name to use.
This is useful for any class and method, not just the Bundle:Controller:Action syntax in the FrameworkBundle
@pierredup I like this PR, so let's see if we can get it merged. This is the last week before the Symfony 2.6 feature freeze, so if we can work quickly, then this could get in for 2.6 :). The biggest issue I see is that there is a lot of logic here to get the message right, which makes it look disorganized. Perhaps if we moved a lot (or all) of this logic into different private functions, it'll help make things look more clear. Specifically, if all the logic fromline 92 toline 137 were in a private function, then all the nested if's would be easier to read. I actually just have a hard time following which Thanks! |
43c8666
to16743fe
Compare$container->expects($this->once()) | ||
->method('get') | ||
->with('foo') | ||
->will($this->returnValue($this)) | ||
; | ||
->will($this->returnValue($this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Did you mean to make these changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This was done according to fabbot.io (http://fabbot.io/report/symfony/symfony/10788/16743fe4ac4b5929409b9f79e8349bb2a3460a9a, but the page doesn't exist anymore). I just applied the patch, and assumed the changes is correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
ah, then you're fine! Thanks for the clarification!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@pierredup Sorry about that but fabbot (and actually PHP-CS-Fixer) is sometimes wrong, and here it is definitely wrong.
@pierredup There are a few CS changes that I think you made accidentally and the PR needs to be rebased. But after that, 👍 from me - I think it's certainly a big improvement. |
8bc4fce
to23b81a8
CompareI've simplified the check a bit more and squashed the commits, so I think this PR is ready now |
@@ -155,4 +159,52 @@ protected function createController($controller) | |||
return array(new $class(), $method); | |||
} | |||
private function getControllerError(array $callable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
That's wrong. ThecreateController()
method can return any PHP callable. It just happens that the default implementation returns an array, but that's not a requirement.
dd48191
to1cabe34
Compare@pierredup I see you made the change fabpot suggested - awesome! Can you rebase if you have a chance to fix the conflict? |
$method, | ||
$reflection->getName() | ||
); | ||
} elseif (is_string($callable) && !function_exists($callable)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
if
is enough given that the previousif
returns.
@pierredup Do you have some time to finish this PR? |
1cabe34
to875d342
Compare@fabpot Done |
875d342
toa17ea7b
Compareping@fabpot |
Ping @symfony/deciders - failing tests seem unrelated |
👍 All the edge-cases for different types of controllers is quite complex, but I couldn't think of any other edge-cases, and if there are some, it will just cause a less-helpful error message that we can improve on. |
Status: Reviewed |
👍 |
$reflection = new \ReflectionClass($controller); | ||
if (!method_exists($controller, $method)) { | ||
$collection = array_map( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Why not useget_class_methods()
here?
e6d8bc5
to40690cc
Compare@pierredup Can you have a look at the tests. They are broken. |
$message .= $this->getControllerError($callable); | ||
throw new \InvalidArgumentException($message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Can you remove the intermediate$message
var and do everything on one line?
40690cc
to33e4482
Compare@fabpot I'm looking into the failing builds, will let you know once the builds are passing |
private function getControllerError($callable) | ||
{ | ||
if (is_string($callable)) { | ||
if (!function_exists($callable)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
this is broken forFoo::bar
strings, as such functions won't exist and so will go there. the check for::
needs to be done first
0fa35ae
toc7ff100
Comparec7ff100
toe0e19f6
Comparearray( | ||
'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar', | ||
'\InvalidArgumentException', | ||
'Controller "Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar"for URI "/" is not callable.', | ||
'/.?[cC]ontroller(.*?)for URI "\/" is not callable\.( Expected method(.*) Available methods)?/', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The regular expression test is necessary because on travis when the tests are run using deps=low the tests failed because of the installed dependecies
Travis tests is now passing. The failures on appveyor looks unrelated to these changes. |
Thank you@pierredup. |
…action isn't callable (pierredup)This PR was merged into the 3.0-dev branch.Discussion----------[HttpKernel] Add better error message when controller action isn't callable| Q | A| ------------- | ---| Bug fix? | no| New feature? | yes| BC breaks? | no| Deprecations? | no| Tests pass? | yes| Fixed tickets | none| License | MIT| Doc PR |In the `ControllerResolver`, if a controller isn't callable, try to give a better description of what went wrongCommits-------e0e19f6 Add better error message when controller action isn't callable
In the
ControllerResolver
, if a controller isn't callable, try to give a better description of what went wrong