PHP get_called_class () альтернатива

У меня есть абстрактный суперкласс PHP, который содержит код, который должен знать, какой подкласс работает под ним.

class Foo { static function _get_class_name() { return get_called_class(); //works in PHP 5.3.*, but not in PHP 5.2.* } static function other_code() { //needs to know echo self::_get_class_name(); } } class Bar extends Foo { } class FooBar extends Foo { } Bar::other_code(); // i need 'Bar' FooBar::other_code(); // i need 'FooBar' 

Это будет работать, если я get_called_class() функцию get_called_class() – однако этот код будет запущен в PHP версии 5.2. *, Так что функция недоступна.

Существуют некоторые пользовательские реализации PHP get_called_class() , но все они полагаются на debug_backtrack() , анализ имени файла и номера строки и запуск регулярного выражения (поскольку кодер не знает, что PHP 5.2 имеет отражение), чтобы найдите имя класса. Этот код должен быть запущен с php, т. Е. не только из файла .php. (Он должен работать из оболочки php -a или оператора eval() .)

В идеале решение будет работать, не требуя добавления какого-либо кода в подклассы … Единственное возможное решение, которое я вижу, заключается в добавлении следующего кода в каждый подкласс, что, очевидно, является отвратительным взломом:

 class FooBar extends Foo { static function _get_class_name() { return 'FooBar'; } } 

EDIT: Подождите, это даже не работает. Это было бы моим последним прибежищем. Может ли кто-нибудь подумать о чем-то подобном этому решению, которое обеспечило бы мне необходимую функциональность. Т.е., я готов принять решение, которое требует от меня добавить одну функцию или переменную в каждый подкласс, рассказывая ей, что такое его имя класса. К сожалению, кажется, что вызов self::_get_class_name() из суперкласса вызывает реализацию родительского класса, даже если подкласс его переопределил.

В действительности часто полезно знать фактический вызываемый (под) класс при выполнении метода суперкласса, и я не согласен с тем, что есть что-то не так, что вы хотите решить эту проблему.

Например, мои объекты должны знать имя класса, но то, что они делают с этой информацией, всегда одинаково и может быть извлечено в метод суперкласса, если бы я смог получить имя вызываемого класса. Даже команда PHP подумала, что это достаточно полезно для включения в php 5.3.

Правильный и непроповедный ответ, насколько я могу судить, заключается в том, что до 5.3 вам нужно либо сделать что-то отвратительное (например, backtrace), либо просто включить дублирующий код в каждый из подклассов.

Рабочее решение:

 function getCalledClass(){ $arr = array(); $arrTraces = debug_backtrace(); foreach ($arrTraces as $arrTrace){ if(!array_key_exists("class", $arrTrace)) continue; if(count($arr)==0) $arr[] = $arrTrace['class']; else if(get_parent_class($arrTrace['class'])==end($arr)) $arr[] = $arrTrace['class']; } return end($arr); } 

Это невозможно.

Концепция «называемого класса» была представлена ​​в PHP 5.3. Эта информация не отслеживалась в предыдущих версиях.

В качестве уродливой работы вы можете использовать debug_backtrace для поиска стека вызовов, но это не эквивалентно. Например, в PHP 5.3 использование ClassName::method() не пересылает статический вызов; вы не можете сказать это с помощью debug_backtrace . Кроме того, debug_backtrace является относительно медленным.

Альтернатива PHP / 5.2 поздней статической привязки, которая позволяет дублировать код до минимума, избегая при этом странных хаков, заключается в создании однострочных элементов на дочерних классах, которые передают имя класса в качестве аргумента:

 abstract class Transaction{ public $id; public function __construct($id){ $this->id = $id; } protected static function getInstanceHelper($class_name, $id){ return new $class_name($id); } } class Payment extends Transaction{ public static function getInstance($id){ return parent::getInstanceHelper(__CLASS__, $id); } } class Refund extends Transaction{ public static function getInstance($id){ return parent::getInstanceHelper(__CLASS__, $id); } } var_dump( Payment::getInstance(1), Refund::getInstance(2) ); 
 object(Payment)#1 (1) { ["id"]=> int(1) } object(Refund)#2 (1) { ["id"]=> int(2) } 

Решение:

 get_class($this); 

Однако я не знаю, работает ли это предложение в статических функциях. Попробуйте и скажите мне свои отзывы.

Этот хак включает в себя отвратительное использование debug_backtrace … не очень, но он выполняет эту работу:

 <?php function callerName($functionName=null) { $btArray = debug_backtrace(); $btIndex = count($btArray) - 1; while($btIndex > -1) { if(!isset($btArray[$btIndex]['file'])) { $btIndex--; if(isset($matches[1])) { if(class_exists($matches[1])) { return $matches[1]; } else { continue; } } else { continue; } } else { $lines = file($btArray[$btIndex]['file']); $callerLine = $lines[$btArray[$btIndex]['line']-1]; if(!isset($functionName)) { preg_match('/([a-zA-Z\_]+)::/', $callerLine, $matches); } else { preg_match('/([a-zA-Z\_]+)::'.$functionName.'/', $callerLine, $matches); } $btIndex--; if(isset($matches[1])) { if(class_exists($matches[1])) { return $matches[1]; } else { continue; } } else { continue; } } } return $matches[1]; } 

Я задал такой вопрос раньше, потому что хотел, чтобы у родителя был заводский метод, который был чем-то вроде этого

 public static function factory() { return new __CLASS__; } 

Но он всегда возвращал родительский класс, а не унаследованный.

Мне сказали, что это невозможно без поздней статической привязки . Он был введен в PHP 5.3. Вы можете прочитать документацию .

Эта функция выполняет ту же работу, но также работает с экземплярами:

 if (!function_exists('get_called_class')) { function get_called_class() { $bt = debug_backtrace(); /* echo '<br><br>'; echo '<pre>'; print_r($bt); echo '</pre>'; */ if (self::$fl == $bt[1]['file'] . $bt[1]['line']) { self::$i++; } else { self::$i = 0; self::$fl = $bt[1]['file'] . $bt[1]['line']; } if ($bt[1]['type'] == '::') { $lines = file($bt[1]['file']); preg_match_all('/([a-zA-Z0-9\_]+)::' . $bt[1]['function'] . '/', $lines[$bt[1]['line'] - 1], $matches); $result = $matches[1][self::$i]; } else if ($bt[1]['type'] == '->') { $result = get_class($bt[1]['object']); } return $result; } } 
 <?php class Foo { private static $instance; static function _get_class_name() { return self::myNameIs(); } static function other_code() { //needs to know echo self::_get_class_name(); } } class Bar extends Foo { public static function myNameIs() { self::$instance = new Bar(); return get_class(self::$instance); } } class FooBar extends Foo { public static function myNameIs() { self::$instance = new FooBar(); return get_class(self::$instance); } } Bar::other_code(); // i need 'Bar' FooBar::other_code(); // i need 'FooBar'