在php 5.3之前伪造后期静态绑定

时间:2009-05-20 22:08:10

标签: php oop static

我需要一个继承的静态函数“call”来调用另一个被覆盖的静态函数“inner”。我可以用后期静态绑定来做到这一点,但我的主机还没有php5.3,所以我需要解决它。

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();

我希望输出为:
    A级= A级
    B类= B类

但它是什么:
    A级= A级
    B类= A类

我的直觉告诉我,我应该能够在call()中写一些内容,以便在调用“call()”时检测引用的对象。因此,而不是self :: inner(),它会沿着calledclass :: inner()的方向行进。检测要从原始方法调用调用的inner()的正确版本。

6 个答案:

答案 0 :(得分:3)

如果性能不是问题,可以使用debug_backtrace()来查找被调用的类:

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

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

答案 1 :(得分:2)

这是一个简单的例子。

<?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();

?>

如果您不想传递类名,可以向子类添加一个静态init函数,并将其显式传递给子类。这将允许您执行以下操作:ClassA :: init() - &gt; call()和ClassB :: init() - &gt; call()但会有一些次要代码重复。

答案 2 :(得分:1)

不幸的是,没有好的方法可以做到这一点(否则PHPers不会为这个功能欢呼这么多)。

必须传递类名。 PHP&lt; 5.3对于具有动态类名的静态调用也没有很好的语法,这使整个事情变得更加丑陋:

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

如果您可以更改代码(而不是使用static),那么单身人士会让它变得更加可忍受。您可以使用帮助功能来简化语法:

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

X函数应该查找,创建并返回类的实例。

答案 3 :(得分:1)

您可以使用对象实例而不是类。如果需要全局符号,可以使用全局变量。因为它们在PHP中相当笨重,所以一个技巧是将它包装在一个函数中。例如:

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();

但更好的想法可能是完全避免全球符号;它在Ruby / Rails中运行良好的原因是Ruby并没有像PHP那样拥有静态。一个类可以反弹并在运行时添加,这样可以轻松扩展框架。在PHP中,类总是最终的,因此在应用程序代码中引用它们是非常强烈的耦合程度。

答案 4 :(得分:1)

通常,当子进程调用父进程的方法时,需要进行后期静态绑定,而父进程又调用子进程的抽象静态方法。我是这样的一个帖子,并且不能使用static ::因为我的PHP不是5.3版本(或更高版本)。以下通过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();

答案 5 :(得分:0)

由于您无法使用static ::,或get_called_class()或__callStatic, 你必须用一些指示来调用inner()函数 被叫类(如其他答案中所述)。一个实例 被叫的班级没问题。 您可以添加“伪静态”方法来模仿每个静态方法 你需要覆盖。这会使您的代码加倍,但是 通过这样做,我希望代码更容易升级一次 php5.3出现:只需删除所有 ps 方法和所有 函数引用它们(并在需要的地方将“self”更改为“static”。)

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();