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

Related of "Faking Late Static Binding перед php 5.3"

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

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

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

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

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

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

К сожалению, нет хорошего способа сделать это (иначе 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 должна искать, создавать и возвращать экземпляр класса.

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

Поскольку вы не можете использовать static ::, или get_called_class () или __callStatic, вам придется вызвать функцию inner () с некоторым указанием с вызываемым классом (как указано в других ответах). Экземпляр вызываемого класса будет в порядке. Вы можете добавить методы «Pseudo Static», чтобы имитировать все статические методы, необходимые для перезаписи. Это удваивает ваш код, но, делая это, как показано ниже, я надеюсь, что код будет легче обновляться после того, как появится 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();