17

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.

hek2mgl
  • 133,888
  • 21
  • 210
  • 235
Dennis
  • 6,954
  • 8
  • 53
  • 97

3 Answers3

15

__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');
hek2mgl
  • 133,888
  • 21
  • 210
  • 235
  • How does `Test::notFound();` trigger a fatal error? Maybe you forgot a `default:` case to do that? – AbraCadaver Nov 07 '13 at 16:31
  • Don't get you. Can you elaborate? (Or just try the code and see. It's pretty self-explaining) – hek2mgl Nov 07 '13 at 16:34
  • you are right of course. my fault.. because of the presence of `__callStatic()` no fatal is thrown. Removed that. Was a bad example. – hek2mgl Nov 07 '13 at 16:37
  • alright ... so in this case we basically side-step the language's mechanism to protect its protected myProtected() function, by allowing a different mechanism __callStatic to use its facilities to call it anyway. But why use `call_user_func_array`? Why not just use `return self::myProtected($args);`? It will do the same thing I think, except the side-effect is that with `self::myProtected($args);` $args parameter remains an array(), where with `call_user_func_array` it magically becomes a string..., as evident by var_dump() output. – Dennis Nov 07 '13 at 19:49
  • `return self::myProtected($args);` will not work as `myProtected()` may consume params like `($a, $b, $c)` but `$args` is always an assoc array. Btw, why not just reading the manual and play around? seems your attempts are too theoretical – hek2mgl Nov 07 '13 at 23:35
  • 1
    I downvoted - "[you] should go on learning the basics" is incredibly assuming and condescending, especially for a reasonable question about the architecture of a language. – webstackdev Mar 29 '19 at 00:00
  • I agree that the sentence wasn't useful. I removed it – hek2mgl Mar 29 '19 at 06:18
  • 1
    Thanks, I changed to upvote, and thanks for the answer also – webstackdev Apr 01 '19 at 17:59
  • The method is just pure genius! – Moses Ndeda Mar 03 '20 at 16:39
4

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.

Dennis
  • 6,954
  • 8
  • 53
  • 97
1

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.

Marlies
  • 919
  • 1
  • 5
  • 17
  • I think I was puzzled because languages like C++ do not have such "catchall" functions ... I think the only new thing PHP has here is the catchalls. I just rewrote what you have above in C++ didn't realize it could do that too, just without the catchalls. – Dennis Nov 07 '13 at 18:53
  • so the static bar() method it is almost like a block of code to call whenever you want, using a shorter "alias" of Foo:bar(); In other words, I could take the bar function out of Foo, and move it into the global scope, and call it as bar(); But having bar in Foo is more so to show that bar belongs to Foo somehow in an object-oriented sense. As to why one would instantiate another object inside bar(), I do not know, but is probably based on certain use-cases, i.e wanting to avoid writing instantiation code and hiding it behind a call to a static function, because as in my example, it's shorter. – Dennis Nov 07 '13 at 19:02
  • Basically in PHP you have regular functions (outside of class declarations), object methods (functions inside a class definition) and static methods (functions inside a class definition with keyword 'static'). Object methods either return something based on the state of the object or alter the state. Class methods are related to the class (and could for example return a specific kind of instance of the object). – Marlies Nov 09 '13 at 11:09