__callStatic(): instantiating objects from static

2019-06-15 14:13发布

问题:

I am confused about how "static" and "dynamic" functions and objects in PHP work together especially with regards to __callStatic().

How __callStatic() works:

You can have a normal class MyClass, where within the class you can put a static function called __callStatic(), which gets called only when MyClass doesn't have a static function by the name you want.

i.e. I call MyClass::newFunction();

newFunction() is called statically but MyClass does not have it declared. So, then __callStatic() gets called and inside you can say

$myObject=new SomeOtherClass();
$myObject->newFunction();

which calls the function you wanted but on some other object.

Short Version:

In other words, __callStatic() does this:

MyClass::newFunction();

which is hiding this:

(new SomeOtherClass())->newFunction();

Say what now? What looks like code calling a static function from a class, turns out to be calling that function from some other class and calling it via instantiation, and not statically.

Explain this, please!

Why was it done? Can you do anything like this elsewhere, like C++ or Java? I am looking for short & concise, but informative explanation on static and dynamic functions in languages, and in this case whether __callStatic() violates or conforms to the big picture of Language constructs. Or is it a new language construct entirely.

回答1:

__callStatic() provides developers with possibility to react on static method calls even if that methods don't exist or aren't accessible from outside of the class ( being protected). This is useful for dynamic, generic code generation.


Example: You have this class:

class Test {

    protected static function myProtected($test) {
        var_dump(__METHOD__, $test);
    }

    public static function __callStatic($method, $args) {
        switch($method) {
            case 'foo' :
                echo 'You have called foo()';
                var_dump($args);
                break;

            case 'helloWorld':
                echo 'Hello ' . $args[0];
                break;

            case 'myProtected':
                return call_user_func_array(
                    array(get_called_class(), 'myProtected'),
                    $args
                );
                break;                      
        }

    }

}

Try to call:

// these ones does not *really* exist
Test::foo('bar');
Test::helloWorld('hek2mgl');

// this one wouldn't be accessible
Test::myProtected('foo');


回答2:

Why was it done?

This is an existential question, but I think that the answer is "because you can". PHP is a dynamic language and these constructs __call and __callStatic() show off its power, and this power can be useful in some situations.

Can you do anything like this elsewhere, like C++ or Java?

No. They have no similar magic methods.

I am looking for short & concise, but informative explanation on static and dynamic functions in languages

Static functions encapsulate code. They exist even when no class has been instantiated. You can call them using scope resolution operator. Dynamic functions are well, dynamic

.. does __callStatic() violate or conform to the big picture of Language constructs. Or is it a new language construct entirely.

It is a construct of a dynamic language. Not sure if this functionality exists in all dynamic languages, but it does in PHP. It does not violate anything, just introduces new paradigm of fall-through catch-all functions when the function you do call does not exist/not accessible in current scope.



回答3:

I'm not entirely sure why __callStatic() is relevant here?

I don't quite see the difference between:

class Foo {

  static function bar() {
    $obj = new Foo();
    $obj->quux();
  } 

  function quux() {
    return "whatever";
  }

}

and your example? In both scenarios you're calling a static method which is calling another method on an object of the same class.

Yeah, that's possible. Actually doing it suggests you might want to refactor your code though. In the example you're instantiating an object with its default state, executing a method on it and then throwing the object away. This suggests whatever the method is doing doesn't actually need the objects state. That means it either doesn't belong in this class or could simply be rewritten to be a static method itself.

And are you aware of __call? It does the same thing as __callStatic but for objects rather than classes. E.g. $foo->myMissingMethod(); would go to __call if such a method exists for the class of which $foo is an instance.