-
-
Notifications
You must be signed in to change notification settings - Fork 9.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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 the ControllerResolver::createController
method inside of the FrameworkBundle (as the AcmeDemoBundle: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 is Expected method "%s" on class "%s"
. So saying something like Expected 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 from line 92 to line 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
to
16743fe
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
to
23b81a8
Compare
I'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. The createController()
method can return any PHP callable. It just happens that the default implementation returns an array, but that's not a requirement.
dd48191
to
1cabe34
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 previous if
returns.
@pierredup Do you have some time to finish this PR? |
1cabe34
to
875d342
Compare
@fabpot Done |
875d342
to
a17ea7b
Compare
ping @fabpot |
a17ea7b
to
e6d8bc5
Compare
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 use get_class_methods()
here?
e6d8bc5
to
40690cc
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
to
33e4482
Compare
@fabpot I'm looking into the failing builds, will let you know once the builds are passing |
private function getControllerError($callable) | ||
{ | ||
if (is F438 _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 for Foo::bar
strings, as such functions won't exist and so will go there. the check for ::
needs to be done first
0fa35ae
to
c7ff100
Compare
c7ff100
to
e0e19f6
Compare
array( | ||
'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 m 7ABF essage 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 wrong Commits ------- 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