Static::? More like Lies::

In my professional work, we have a system that utilizes __call and __callStatic for caching of certain method calls. We have been running into a problem where calling an undefined method via :: within the class itself will trigger __call rather than the expected __callStatic.

The Problem

The following code example illustrates the issue.

<?php

class foo {
function __call($name, $args){
echo "__call " . $name . PHP_EOL;
}

public static function __callStatic($name, $args) {
echo "__callStatic " . $name . PHP_EOL;
}

public function make_call_self() {
self::ted();
}

public function make_call_static() {
static::ted();
}

function make_call_classname() {
foo::bob();
}
}

$x = new foo();
$x->make_call_self();
$x->make_call_static();
$x->make_call_classname();
foo::sam();

The above returns the following when executed in PHP 5.3+ (excluding PHP 5.3.3, we'll get to this later)

__call ted
__call ted
__call bob
__callStatic sam

See Example

As you can see, all of those ideally should have triggered __callStatic but all save the last foo:::sam() triggered call. If you click the link to the example execution above, it shows that this did in fact work in PHP 5.3.3 but has since been broken.

I was just about to post this as a bug on PHP.net, but my friend Joel Clermont brought to my attention several cases of this being submitted in the past. One example being fairly infamous. Remember how I mentioned it working in 5.3.3? It was actually fixed in that release in response to a bug report. The problem apparently is that it broke a lot of things to do with the at the time new late static binding, and was rolled back in the next release.

From the horses mouth, via colder@php.net

You're misunderstanding the meaning of static::foo();

it doesn't mean "call foo statically" but rather "use runtime information to get the class currently called", and do a static(or not) call to the method of that class.

So static doesn't mean static apparently.

Understanding

After quite a bit of reading and a fair deal of explination by my friend Joel, I came to understand that ::, unlike C++, doesn't mean static at all. Rather it is a "Scope Resolution Operator" which determines scope from "context" meaning that when called from within an instance of an object, it makes instance calls if possible, rather than static calls. Infact, in 5.3+ you can even do $this::. The consequence of this is that self:: nor static:: are necessarily static calls.

This all rubs me wrong; I don't see the benefit to this behavior on the users end. Perhaps there is some backend optimization this allows. Even if that were the case I still feel like you could short circuit undefined self:: calls to __callStatic without breaking it.

Solutions

There were several "solutions" given, none of which are ideal.

Call __callStatic directly.

Use forward_static_call.

Call the class from within a lambda. (After some testing I see that this does not work in PHP 5.4+)

None of these are particularly clean nor ideal. Instead I would like PHP to either add a new call like stat:: or simply fix self:: and static:: to trigger the proper call. The latter of course would be ideal, especially as static has the word static in its name.

See