How to dynamically fetch class name of called class in parent:: call?

334 Views Asked by At

I'm trying to use get_called_class() on a parent class, and retrieve the parent class name rather than the subclass name. I can't use __CLASS__ in this case because I need it in a dynamic context since these methods are actually being inserted into an existing class using Closure::bind and __call.

Quick example:

<?php 
    class Test {
        function doSomething() {
            echo(get_called_class() . ",");
        }
    }

    class TestSuper extends Test {
        function doSomething() {
            echo(get_called_class() . ",");
            parent::doSomething();
        }
    }

    $test = new TestSuper();
    $test->doSomething();
?>

This example prints TestSuper,TestSuper,, but I want something other than get_called_class() which dynamically gets the class where the method exists (printing TestSuper,Test,) rather than the class which was called. It also must work inside the magic __call method. Is this possible?

Update: Better example which more closely reflects what I'm doing: I have a list of methods which I want to apply to an instance (or class) at runtime (in the $addons variable):

<?php
    $addons = array(
        "A" => array(
            "doSomething" => function () { return 10; }
        ),
        "B" => array(
            "doSomething" => function () { return parent::doSomething() + 1; }
        )
    );

    class A {
        private $methods = array();

        function __construct() {
            global $addons;

            $class = get_class($this);

            do {
                foreach ($addons[$class] as $name => $method) {
                    $this->methods[$class][$name] = Closure::bind($method, $this, $class);
                }

                $class = get_parent_class($class);
            } while ($class !== false);
        }

        function __call($name, $arguments) {
            $class = get_class($this); // Or something else...

            do {
                if (isset($this->methods[$class][$name])) {
                    return call_user_func_array($this->methods[$class][$name], $arguments);
                }

                $class = get_parent_class($class);
            } while ($class !== false);
        }
    }

    class B extends A {

    }

    $b = new B();
    echo($b->doSomething() . "\n");
?>

I'm adding a method doSomething to A, and overriding it on B, but there I want to call parent::doSomething() which will execute A's doSomething. Unfortunately, when it comes to executing the parent method, I can't find a way to know that I want to run A's method instead, so it always runs B's and eventually runs out of stack frames. How can I know in the inner __call method to execute the superclass method?

1

There are 1 best solutions below

1
On

You can use __CLASS__ in __call().
In closures, just use get_called_class.

<?php 
    class Test {
        public function a() {
            echo(__CLASS__ . ",");
        }
        public function __call($name, array $args) {
            echo(__CLASS__ . ",");
        }
    }

    class TestSuper extends Test {
        public function c() {
            echo(__CLASS__ . ",");
        }
    }

    $test = new TestSuper();

    $d = Closure::bind(
        function () { echo(get_called_class() . ","); },
        $test,
        $test
    );

    $test->a();
    $test->b();
    $test->c();
    $d();
?>

Outputs: Test,Test,TestSuper,TestSuper,