Reputation: 33
Hello fellow byte crunchers,
last week we stumbled on a somewhat unexpected behavior in PHP. For our framework, we utilize the __call and __callStatic magic functions in a base class to provide some sort of functionality unrelevant for this thread. When we utilize the __callStatic function with an appropriate static call on the same class the magic function is defined while being in an object scope, for some reason, the __call method will get invoked.
Here is a minimal working example:
/** tests calls on class B */
class B {
/** catch object calls on B */
public function __call($name, $arguments) {
print(" __CALL on B\r\n");
}
/** catch static calls on B */
public static function __callStatic($name, $arguments) {
print(" __STATICCALL on B\r\n");
}
}
/** tests calls on class A */
class A {
/** catch object calls on A */
public function __call($name, $arguments) {
print(" __CALL on A\r\n");
}
/** catch static calls on A */
public static function __callStatic($name, $arguments) {
print(" __STATICCALL on A\r\n");
}
public static function doMagicCall_from_staticContext() {
// static scope
A::BLA(); // expect A::__callStatic, works
B::BLA(); // expect B::__callStatic, works
}
public function doMagicCall_from_objectContext() {
// object scope
A::BLA(); // expect A::__callStatic, got $this->__call(), prints "__CALL on A"
B::BLA(); // expect B::__callStatic, works
}
}
$a = new A();
print("1. Call from static context:\r\n");
A::doMagicCall_from_staticContext();
print("\r\n");
print("2. Call from object context:\r\n");
$a->doMagicCall_from_objectContext(); // provokes the behavior
This will print:
1. Call from static context:
__STATICCALL on A
__STATICCALL on B
2. Call from object context:
__CALL on A
__STATICCALL on B
We expected all of the calls to return "__STATICCALL". At first glance this seemed to us as a bug in the interpreter because the PHP documentation clearly states:
__call() is triggered when invoking inaccessible methods in an object context.
We tested different interpreters and so far this behavior is consistent across our tests with: 7.3.10-nts-Win32-VC15-x64, 5.6.40-nts-Win32-VC11-x86 and 8.0.10-nts-Linux-x64
I couldn't find anything related to that at bugs.php.net
My question here is: Is this a bug in the interpreter or did we miss out on something here?
Thanks in advance and greetings from Germany.
Upvotes: 2
Views: 95
Reputation: 33
Seems like i had a misconception of how the :: (Paamayim Nekudotayim, or rather scope resolution operator) actually works. There are still inconsistencies when using the forward_static_call in the same case. Follow up https://bugs.php.net/bug.php?id=81436
Upvotes: 1
Reputation: 54
I cannot supply an answer as to why this happens, and also don't have a solution for your problem. I do however speculate that this is implemented like this because a call to a parent method is done with a static call notation, and supposed to be triggered on the $this of an object. Suppose you had a code setup like the following:
class A {
function do() {
echo "A do";
}
}
class B extends A {
function do() {
parent::do();
echo "B do";
}
}
$b = new B();
$b->do();
The output of this would be:
A doB do
Even though this might be the reason, i think this is unintended and quite possibly a bug. Go ahead and file a bug report on bugs.php.net, i think you're onto something here.
Upvotes: 2