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