Faking Late Static Binding перед php 5.3

Мне нужна унаследованная статическая функция "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(), чтобы определить, на какой объект ссылались, когда был вызван вызов(). Таким образом, вместо self:: inner() это было бы что-то вроде строк classclass:: inner(). Обнаружение правильной версии inner() для вызова из исходного вызова метода.

Ответ 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 классы всегда являются окончательными, поэтому, ссылаясь на них в коде приложения, очень сильная степень сцепления.

Ответ 2

Если производительность не является проблемой, вы можете использовать debug_backtrace(), чтобы найти вызываемый класс:

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

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

Ответ 3

Вот краткий пример.

<?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() → call() и ClassB:: init() → call(), но будет иметь небольшое дублирование кода.

Ответ 4

К сожалению, нет хорошего способа сделать это (иначе PHPers не будут так сильно подходить для этой функции).

Вам нужно передать имя класса. PHP < 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 должна искать, создавать и возвращать экземпляр класса.

Ответ 5

Часто поздняя статическая привязка необходима, когда ребенок вызывает метод родителя, который, в свою очередь, вызывает абстрактный статический метод дочернего элемента. Я был в таком постулите и не мог использовать 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();

Ответ 6

Поскольку вы не можете использовать static:: или get_called_class() или __callStatic, вам нужно будет вызвать функцию inner() с некоторым указанием с помощью называемый класс (как упоминалось в других ответах). Экземпляр названного класса будет в порядке. Вы можете добавить методы "Pseudo Static", чтобы имитировать все статические методы вам нужно перезаписать. Это удваивает ваш код, но сделав это, как показано ниже, я надеюсь, что код будет проще обновляться один раз php5.3 приходит: просто удалите все методы ps и все функции, ссылающиеся на них (и изменить "я" на "статические", когда это необходимо..)

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