Search code examples
phpreflectionscopedebug-backtrace

Can you resolve late static bindings out of scope in PHP?


I finally found myself learning about scope and context, and the difference between them, but now I'm having problems.

I have a static method in a parent class that's inherited by a child class. When I call the method from the child, I want to be able to tell, in the parent, which child called it.

Late static bindings, right? Cool. Like this:

class Daddy {
    public static function madSkillz() {
        var_dump( static::class );
    }
}

class Sonny extends Daddy {}

Sonny::madSkillz();

And in the output, I see:

string(5) "Sonny"

Hooray, exactly what I was hoping for! BUT... now let's say that I call a function from within that parent static method. I can use debug_backtrace to find out that the static method was called, but unfortunately I see the declaring class, rather than the scope.

function GoToTheFair() {
    var_dump( debug_backtrace() );
}

class Daddy {
    public static function madSkillz() {
        var_dump( static::class );
        GoToTheFair();
    }
}

class Sonny extends Daddy {}

Sonny::madSkillz();

This prints the output:

string(5) "Sonny"
array(2) {
  [0]=>
  array(4) {
    ["file"]=>
    string(28) "/home/branja/Desktop/cry.php"
    ["line"]=>
    int(10)
    ["function"]=>
    string(11) "GoToTheFair"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(6) {
    ["file"]=>
    string(28) "/home/branja/Desktop/cry.php"
    ["line"]=>
    int(16)
    ["function"]=>
    string(9) "madSkillz"
    ["class"]=>
    string(5) "Daddy"
    ["type"]=>
    string(2) "::"
    ["args"]=>
    array(0) {
    }
  }
}

I want to be able to create a ReflectionMethod object in GoToTheFair() and invoke other methods, but I need to be sure I'm using the proper scope, which should be Sonny, not Daddy.

Is there a way to do this without passing static::class as an argument to GoToTheFair()?


EDIT: The reason I do not want to use the argument approach is that the GoToTheFair() function is the library I'm coding and the classes Daddy and Sonny are the user's. I do not want them to have the freedom to choose their own scope.


Solution

  • It seems to be impossible to get what you want (in this case) because debug_backtrace() uses the constant __class__ to display the class name.

    In your context :

    public static function madSkillz() {
        var_dump( static::class ); // outputs : "Sonny"
        var_dump( __class__ ); // outputs : "Daddy"
        GoToTheFair();
    }
    

    Possible solution 1:

    But, you could get the proper class if the caller is an object, not a static method. Here is an exemple using an object :

    function GoToTheFair() {
        $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT) ;
        $last  = $trace[1] ;
        var_dump(get_class($last['object'])) ;
    }
    class Daddy {
        public function madSkillz() {
            var_dump( static::class );
            GoToTheFair();
        }
    }
    class Sonny extends Daddy {}
    (new Sonny)->madSkillz();
    

    Will outputs :

    "Sonny"
    "Sonny"
    

    Possible solution 2:

    You could be to use the Singleton Pattern.

    function GoToTheFair() {
        $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT) ;
        $last  = $trace[1] ;
        var_dump(get_class($last['object'])) ;
    }
    class Daddy {
    
        private static $instance = null;
        private function __construct() {}
    
        public static function getInstance() {
            $class = static::class ;
            if (!isset(self::$instance))
                self::$instance = new $class();
            return self::$instance ;
        }
        public function madSkillz() {
            var_dump( static::class );
            GoToTheFair();
        }
    }
    class Sonny extends Daddy {}
    
    Sonny::getInstance()->madSkillz();
    

    Will outputs :

    "Sonny"
    "Sonny"