Faking Late Static Binding before php 5.3

2020-03-01 09:35发布

I need an inherited static function "call" to call another static function "inner" that has been overridden. I could do this with late static binding, but my host does not have php5.3 yet and so I need to work around it.

class ClassA{
    static function call()
    {
        return self::inner();
    }

    static function inner(){
        return "Class A";
    }   
}

class ClassB extends ClassA{
    static function inner(){
        return "Class B";
    }
}

echo "<p>Class A = " . ClassA::call();
echo "<p>Class B = " . ClassB::call();

I would like the output to be:
Class A = Class A
Class B = Class B

But what it is:
Class A = Class A
Class B = Class A

My gut tells me that I should be able to write something in call() to detect what object was referenced when "call()" was, well, called. So instead of self::inner() it would so something along the lines of calledclass::inner(). Detecting the proper version of inner() to call from the original method call.

标签: php oop static
6条回答
爷、活的狠高调
2楼-- · 2020-03-01 10:01

You can use object instances rather than classes. If you want a global symbol, you can use a global variable. Since they are rather unwieldy in PHP, one trick is to wrap it in a function. Eg.:

class ClassA {
  function call() {
    return $this->inner();
  }
  function inner() {
    return "Class A";
  }   
}
function ClassA() {
  static $instance;
  return $instance ? $instance : new ClassA();
}

class ClassB extends ClassA {
  function inner() {
    return "Class B";
  }
}
function ClassB() {
  static $instance;
  return $instance ? $instance : new ClassB();
}

echo "<p>Class A = " . ClassA()->call();
echo "<p>Class B = " . ClassB()->call();

But a better idea might be to avoid global symbols altogether; The reason why it works well in Ruby/Rails, is that Ruby doesn't really have static state in the same way that PHP has. A class can be rebound and added to at runtime, which allows for easy extension of the framework. In PHP, classes are always final, so referring to them in application code, is a very strong degree of coupling.

查看更多
Rolldiameter
3楼-- · 2020-03-01 10:02

Unfortunately there is no nice way to do it (otherwise PHPers wouldn't cheer so much for that feature).

You have to pass class name. PHP < 5.3 also doesn't have nice syntax for static calls with dynamic class name which makes whole thing even uglier:

static function call($class)
{
    return call_user_func(array($class,"inner"));
}
…
ClassA::call("ClassA");
ClassB::call("ClassB");

If you can change the code (and not use static), then singletons make it more bearable. You can make helper function to ease the syntax:

X("ClassA")->call();
X("ClassB")->call();

The X function should look up, create and return instance of a class.

查看更多
你好瞎i
4楼-- · 2020-03-01 10:16

Here's a quick example.

<?php

class ClassA{
   public function call(){
      return $this->inner();
   }

   static function inner(){
      return "Class A";
   }

   static function init($class){
      return new $class();
   }   
}

class ClassB extends ClassA{
   static function inner(){
      return "Class B";
   }
}

echo "<p>Class A = " . ClassA::init("ClassA")->call();
echo "<p>Class B = " . ClassB::init("ClassB")->call();

?>

If you don't like passing in the class name you could add a static init function to the child class and explicitly pass it there as well. This would allow you to do something like: ClassA::init()->call() and ClassB::init()->call() but would have some minor code duplication.

查看更多
Fickle 薄情
5楼-- · 2020-03-01 10:17

If performance is not an issue, you can use debug_backtrace() to find the called class:

$bt = debug_backtrace();
return get_class($bt[1]['object']); 

http://php.net/manual/en/function.debug-backtrace.php

查看更多
兄弟一词,经得起流年.
6楼-- · 2020-03-01 10:18

Since you cant use static:: , or get_called_class(), or __callStatic, you'll have to call the inner() function with some indication with the called class (as mentioned in the other answers). An instance of the called class would be fine. You can add "Pseudo Static" methods to mimick every static method you need to overwrite. This doubles your code, but by doing this as below, I hope the code is easier to upgrade once php5.3 comes around: just remove all the ps methods and all the functions referencing them (and change "self" to "static" where needed ..)

class ClassA{
    static function call()
    {
        return self::inner();
    }

    static function inner(){
        return "Class A";
    } 

    public function _ps_call()
    {
        return $this->_ps_inner();
    }


}

class ClassB extends ClassA{

    public static function getInstance() 
    {
        return new self();
    }

    public static function call() 
    {
        return self::getInstance()->_ps_call();
    }

    static function inner()
    {
        return "Class B";
    }   

    public function _ps_inner()
    {
        return self::inner();
    }
}

echo "<p>Class A = " . ClassA::call();
echo "<p>Class B = " . ClassB::call();
查看更多
干净又极端
7楼-- · 2020-03-01 10:24

Often, late static binding is needed when a child calls back a method of the parent who, in turn, calls an abstract static method of the child. I was in such a postition, and could not use static:: as my PHP was not version 5.3 (or later). The following accomplished late static binding by way of debug_backtrace().

abstract class ParentClass
{
    static function parent_method()
    {
        $child_class_str = self::get_child_class();
        eval("\$r = ".$child_class_str."::abstract_static();");
        return $r;
    }// CHILD MUST OVERRIDE TO PUT ITSELF INTO TRACE

    protected abstract static function abstract_static(); // THIS NEEDS LATE STATIC BINDING

    private static function get_child_class()
    {
        $backtrace = debug_backtrace();
        $num = count($backtrace);
        for($i = 0; $i < $num; $i++)
        {
             if($backtrace[$i]["class"] !== __CLASS__)
                  return $backtrace[$i]["class"];
        }
        return null;
    }
}

class ChildClass extends ParentClass
{
    static function parent_method(){ return parent::parent_method(); }

    protected static function abstract_static()
    {
        return __METHOD__."()";
    }// From ParentClass
}

print "The call was: ". ChildClass::parent_method();
查看更多
登录 后发表回答