Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__callStatic is not called?

Tags:

php

class

static

I know the following could potentially create problems elsewhere and is probably bad design, but I still would like to know why this fails (for my own edification):

class Test {
    // singleton
    private function __construct(){}
    private static $i;
    public static function instance(){
        if(!self::$i){
            self::$i = new Test();
        }
        return self::$i;
    }
    // pass static requests to the instance
    public static function __callStatic($method, $parameters){
        return call_user_func_array(array(self::instance(), $method), $parameters);
    }
    private $someVar = 1;
    public function getSomeVar(){
        return $this->someVar;
    }
}

print Test::getSomeVar();

The error is Using $this when not in object context

Obviously $this is unavailable from a static method, but the static method is handing it off to an instance method invokation via call_user_func_array, which should make $this the instance...

/EDIT

I'm aware that $this is not available in static context. However, this works:

print call_user_func_array(array(Test::instance(), 'getSomeVar'), array());

Which is exactly what's happening in the __callStatic overload, so something's amiss...

/EDIT 2

scope is definitely getting handled strangely. if you pull the singleton instance to any other class, it works as expected:

class Test {
    // singleton
    private function __construct(){}
    private static $i;
    public static function instance(){
        if(!self::$i){
            self::$i = new Blah();
        }
        return self::$i;
    }
    // pass static requests to the instance
    public static function __callStatic($method, $parameters){
        return call_user_func_array(array(static::instance(), $method), $parameters);
    }
}

class Blah {
    private $someVar = 1;
    public function getSomeVar(){
        return $this->someVar;
    }
}

print Test::getSomeVar();
like image 785
momo Avatar asked Nov 19 '12 03:11

momo


1 Answers

You can not staticfy your object methods through __callStatic calls in PHP. It will only be invoked when the method so far does not exist (incl. not being visible from the calling context). In your case Test::getSomeVar() is already defined.

Because of backwards compatibility, PHP does not check if only a static method exists, but actually if generally a method exists.

In your case you are calling the non-static method statically, so $this is not defined because __callStatic has not been invoked. If you would have enabled warnings and notices to the highest level (recommended for development), PHP would have warned you about that.

The correct usage therefore is:

echo Test::instance()->getSomeVar();

As with any other implementation of a Singleton.

So you are just using __callStatic wrong, it only works for methods not yet defined. Choose another tool/design for the job, like the aggregation example you use with your Blah class.

A further note:

You should normally prevent to use any static context in PHP anyway, especially as you exploit a lot of magic functionality here, too, which is another smell. It looks like you have a pile of design issues before you even finished your code. You will only increase the likelihood to run into hard to debug and maintain code with all this. But that just so you do not say in some time you have not been warned.

See Who needs singletons? in case you want to learn more.

like image 152
hakre Avatar answered Oct 13 '22 03:10

hakre