2

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.

Jared Brandt
  • 203
  • 1
  • 7

1 Answers1

2

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"
Syscall
  • 19,327
  • 10
  • 37
  • 52
  • That's quite unfortunate, because, as I mentioned in my edit, the classes are the client's, and the GoToTheFair() function is my library. Everything else was working just fine, and then I realized it doesn't work with inherited static methods. I'll just have to put a big "sorry, my library doesn't work with inherited static methods" stamp on it :( – Jared Brandt Feb 13 '18 at 08:49
  • @JaredBrandt I'm agree, and sorry. I have looked for ways to solve this problem, but I think there is no way to get the caller instead using `debug_backtrace()`. – Syscall Feb 13 '18 at 08:57
  • I understand. Not your fault PHP implements it this way :) I guess I could just provide an optional parameter that they could pass "static::class" to, and I'll just check using is_a() that the class name they pass as the argument is a child of the class in debug_backtrace (or the same class). Thanks for your help! – Jared Brandt Feb 13 '18 at 09:00