Skip to content

Handle case of static controller method and controllers using magic __call() method #6080

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

Closed
wants to merge 2 commits into from

Conversation

ghost
Copy link

@ghost ghost commented Nov 21, 2012

Improve collecting controller details for edge cases where:

  • controller is array, but contains class name and static method
  • method doesn't exist, but is handled by magic __call() method

$this->data['controller'] = array(
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
'method' => $controller[1],
'file' => 'n/a/',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a trailing / that should be removed.

@fabpot
Copy link
Member

fabpot commented Nov 21, 2012

Can you add some unit tests?

'line' => $r->getStartLine(),
);
} catch (\ReflectionException $re) {
if (is_callable($controller)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the function is_callable will only work if $controller[0] is an object.
So either the line

'class'  => is_object($controller[0]) ? get_class($controller[0]) : $controller[0]

can be changed to

'class'  => get_class($controller[0])

or this line should be changed to

if(is_callable($controller) || method_exists($controller[0], '__call'))

After the if you can also add the folling

$r = new \ReflectionClass($controller[0]);

Then you can add the file name

'file'   => $r->getFilename(),

instead of n/a and for the line you can add

'line'   => $r->getMethod('__call')->getStartLine()

DerManoMann and others added 2 commits November 22, 2012 11:10
* Drop surplus '/' in RequestDataCollector.php
* Add unit test for the various callable forms
@ghost
Copy link
Author

ghost commented Nov 21, 2012

@pierredup
I disagree with the your comment about is_callable() only working with objects. The PHP docs state that the first argument is a callable, so it can be a string, array, closure, and perhaps more.

The test I added also shows that the code works as is :)

I've thought about your suggestion of adding reflection to look up the location of __call(). However, I think this doesn't really add a lot and only complicates matters. Also, as you can see in the new test, there is also __callStatic() to consider.

The fact that file/line are n/a is correct, because the most typical case will be that __call() and __callStatic() will delegate to some other method that might not even be in the same class/file (a subclass I would expect), IMHO.

@fabpot
Good catch about the '/'. I hope the test is complete enough. Looks more like an exercise on PHP callables than anything else, tho ;)

@pierredup
Copy link
Contributor

True that is_callable takes any callable argument, except in the one specific case where you have a __call() method, and pass an array with the first paramater as a string.

Take the following example:

class Controller {
    public function __call($method, $arguments) {}
}

$controller = array('Controller', 'action');

var_dump(is_callable($controller));

Here is_callable($controller) will actually return false, where if you have $controller = array(new Controller, 'action'); it would return true.

Of course if you have a __callStatic method, then it would always return true.

Your tests doesn't seem to cover this use case

@ghost
Copy link
Author

ghost commented Nov 22, 2012

Hmm, maybe. I have to admin that I do not know about this case. OTOH, if is_callable returns false is it really callable then? I would think this more of a PHP bug then?

I think I might have come across this case during coding, but then dismissed it because in that case FilterControllerEvent failed already before the data collector code is reached.

In FilterControllerEvent there is a check on is_callable and a LogicException is thrown if $controller is not callable.

So, is FilterControllerEvent wrong too then?

@pierredup
Copy link
Contributor

One would think that if is_callable returns false, then the controller isn't callable, but in the case I mentioned above, the controller is in fact callable. I also thought it was a bug with php, but the php-internals don't seem to think so.

The problem is, if you specify the class as a string, php looks for a static method, even if you have a __call method, it won't be registered.

I will have a look at the FilterControllerEvent to see if this use case applies there as well.

@ghost
Copy link
Author

ghost commented Nov 22, 2012

Rather strange - if that is the case then using is_callable seems pretty pointless and the only way would be to try to execute the controller to find out if it is, in fact, callable...

@pierredup
Copy link
Contributor

Okay so it actually seems that the case above isn't callable after all. If the controller is specified as a string, then a static method need to exist. Hence why it works with __callStatic. Only when an instance of the class is specified, will it handle the __call method.

@ghost
Copy link
Author

ghost commented Nov 22, 2012

So the tests are sufficient then?

@pierredup
Copy link
Contributor

Yes it is.
This happens when you just assume something without actually testing it :)

Sorry for the hassle

@fabpot fabpot closed this in c8ebc1e Nov 24, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants