Подделка Позднее статическое связывание до php 5.3 - PullRequest
11 голосов
/ 21 мая 2009

Мне нужна унаследованная статическая функция "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();

Я бы хотел, чтобы результат был:
Класс А = Класс А
Класс B = Класс B

Но что это такое:
Класс A = Класс A
Класс B = Класс A

Моя интуиция говорит мне, что я должен быть в состоянии написать что-то в call (), чтобы определить, на какой объект ссылались, когда "call ()" был, ну, конечно, вызван. Таким образом, вместо self :: inner () это будет чем-то похожим на метод callclass :: inner (). Обнаружение правильной версии inner () для вызова из исходного вызова метода.

Ответы [ 6 ]

3 голосов
/ 12 октября 2009

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

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

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

2 голосов
/ 27 января 2012

Вот быстрый пример.

<?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 (), но будет иметь незначительное дублирование кода.

1 голос
/ 24 ноября 2010

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

Вы можете использовать экземпляры объекта, а не классы. Если вам нужен глобальный символ, вы можете использовать глобальную переменную. Поскольку они довольно громоздки в PHP, один из приемов состоит в том, чтобы обернуть их в функцию. 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();

Но лучшей идеей может быть полное отсутствие глобальных символов; Причина, по которой он хорошо работает в Ruby / Rails, заключается в том, что Ruby на самом деле не имеет статического состояния так же, как PHP. Класс может быть восстановлен и добавлен во время выполнения, что позволяет легко расширять структуру. В PHP классы всегда являются окончательными, поэтому обращение к ним в коде приложения является очень сильной степенью связи.

1 голос
/ 21 мая 2009

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

Вы должны передать имя класса. PHP <5.3 также не имеет приятного синтаксиса для статических вызовов с динамическим именем класса, что делает все это еще ужаснее: </p>

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

Если вы можете изменить код (и не использовать static), то синглтоны сделают его более терпимым. Вы можете сделать хелпер function для упрощения синтаксиса:

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

Функция X должна искать, создавать и возвращать экземпляр класса.

0 голосов
/ 24 декабря 2010

Поскольку вы не можете использовать 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...