Patryk
Patryk

Reputation: 33

Unexpected behavior when utilizing __call and __callStatic from different scopes

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

Answers (2)

Patryk
Patryk

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

nerdbeere
nerdbeere

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

Related Questions