6

I have a question about a strange comportement.

Look at this code :

class User
{
    public function can($name) {
        return call_user_func(array($name, 'test'));
    }

    public static function __callStatic($name, $args) {
        return 'User::__callStatic';
    }

    public function __call($name, $args) {
        return 'User::__call';
    }
}

class Foo
{
    public static function __callStatic($name, $args) {
        return 'Foo::__callStatic';
    }

    public function __call($name, $args) {
        return 'Foo::__call?';
    }
}

$u = new User();
var_dump($u->can('User'));
var_dump($u->can('Foo'));

The results of the first var dump is : "User::__call" And the second : "Foo::__callStatic"

Why the first one don't call the function __callStatic ?

PS : I look at other topics, but not found an explaination.

Thanks

2 Answers2

1

This is just a problem of scope when you call the call_user_func.

When you first call the can function you are in the class User context and it will take __call. In the second call your context is from outside the second class, so therefore it takes the __callStatic. Check callback manual and call-user-func.

For instance code:

<?php

class User
{
    public function can($name) {
        return call_user_func(array($name, 'test'));
    }

    public static function __callStatic($name, $args) {
        return 'User::__callStatic';
    }

    public function __call($name, $args) {
        return 'User::__call';
    }
}

class Foo
{
    public function can($name) {
        return call_user_func(array($name, 'test'));
    }

    public static function __callStatic($name, $args) {
        return 'Foo::__callStatic';
    }

    public function __call($name, $args) {
        return 'Foo::__call?';
    }
}

    function can($name) {
        return call_user_func(array($name, 'test'));
}

$u = new User();
$f = new Foo();
var_dump($u->can('User'));
var_dump($u->can('Foo'));
var_dump($f->can('User'));
var_dump($f->can('Foo'));
var_dump(can('User'));
var_dump(can('Foo'));

will return:

string(12) "User::__call" 
string(17) "Foo::__callStatic" 
string(18) "User::__callStatic" 
string(12) "Foo::__call?"
string(18) "User::__callStatic"
string(17) "Foo::__callStatic"
Edwin
  • 2,146
  • 20
  • 26
  • 2
    Scope is the wrong word, *context* is what you're looking for. The question then though is why PHP decides that `call_user_func(['User', ...])` in a `User` context is an instance call, while in another context it's static. – deceze Sep 01 '17 at 13:43
  • yes context is better. If you will made the `can` function static it would return the `__callStatic`, so I think php it's trying to find first the functions on the same level (if this makes sense). – Edwin Sep 01 '17 at 13:50
0

Thank you both of you.

Yes this is due to context , is that a bug of PHP (fix in the later version, not in 7.2, so... ;) ) or is it just a strange comportement.

@Edwin I know if I transform the function can() to static, it will work, but I doesnt want to. To resolve the "problem", I call the function __callStatic() in my function can() only in the case of calling self class :

if($name instanceof User) return self::__callStatic($functionName, $args);