Domanda

I am writing an api in PHP. I have got a base class which implemets the magic function __call:

class Controller
{
    public function __call($name, $arguments)
    {
        if(!method_exists($this,$name))
            return false;
        else if(!$arguments)
            return call_user_func(array($this,$name));
        else
            return call_user_func_array(array($this,$name),$array);
    }
}

and a child class like this:

class Child extends Controller
{
    private function Test()
    {
        echo 'test called';
    }
}

so when i do this:

$child = new Child();
$child->Test();

and load the page it takes a lot of time and after a while the web browser prints that the page can't be requested. no output is given from php, only a web browser error.

apache error log (last part only):

...
[Tue Sep 24 12:33:14.276867 2013] [mpm_winnt:notice] [pid 1600:tid 452] AH00418: Parent: Created child process 3928
[Tue Sep 24 12:33:15.198920 2013] [ssl:warn] [pid 3928:tid 464] AH01873: Init: Session Cache is not configured [hint: SSLSessionCache]
[Tue Sep 24 12:33:15.287925 2013] [mpm_winnt:notice] [pid 3928:tid 464] AH00354: Child: Starting 150 worker threads.
[Tue Sep 24 12:38:43.366426 2013] [mpm_winnt:notice] [pid 1600:tid 452] AH00428: Parent: child process exited with status 3221225725 -- Restarting.
[Tue Sep 24 12:38:43.522426 2013] [ssl:warn] [pid 1600:tid 452] AH01873: Init: Session Cache is not configured [hint: SSLSessionCache]

i can't find the mistake, but if the function Test is protected everything works fine.

solution found:

public function __call($name, $arguments)
{
    if(!method_exists($this,$name))
        return false;
    $meth = new ReflectionMethod($this,$name);
    $meth->setAccessible(true);
    if(!$arguments)
        return $meth->invoke($this);
    else
        return $meth->invokeArgs($this,$arguments);
}
È stato utile?

Soluzione

This behavior is an issue (bug?) documented in the documentation of method_exists(): method_exists() returns true even if the method is private/protected and thus, not accessible from outside the class. This leads to infinite recursion in your case, as your Child->Test() call invokes Child::__call(), which checks whether Test() exists (it does, but can't be called), then tries to call it, which again leeds to __call() being invoked. Comments suggest using get_class_methods() might resolve the issue. I'm not sure why changing the visibility of Test() to private changes the behavior as you stated.

Altri suggerimenti

Give Test() public visibility, and it should work.

I'm not entirely sure why private visibility causes a 500 error (as opposed to Call to private method...), but I suspect it's to do with recursion involving the __call() function. Some features in PHP do more harm than good - do you really need it?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top