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()
- однако этот код будет запущен в 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';
}
}
РЕДАКТИРОВАТЬ: Подождите, это даже не работает. Это было бы моим последним прибежищем. Может ли кто-нибудь подумать о чем-то подобном этому решению, которое обеспечило бы мне необходимую функциональность. Т.е., я готов принять решение, которое требует от меня добавить одну функцию или переменную в каждый подкласс, рассказывая ей, что такое его имя класса. К сожалению, кажется, что вызов self::_get_class_name()
из суперкласса вызывает реализацию родительского класса, даже если подкласс его переопределил.
Ответы
Ответ 1
В действительности часто бывает полезно узнать фактический вызываемый (под) класс при выполнении метода суперкласса, и я не согласен с тем, что что-то не так с необходимостью решить эту проблему.
Например, мои объекты должны знать имя класса, но то, что они делают с этой информацией, всегда одинаково и может быть извлечено в метод суперкласса, если бы я смог получить имя вызываемого класса. Даже команда PHP подумала, что это было достаточно полезно для включения в php 5.3.
Правильный и неубедительный ответ, насколько я могу судить, заключается в том, что до 5.3 вам нужно либо сделать что-то отвратительное (например, backtrace), либо просто включить дублирующий код в каждый из подклассов.
Ответ 2
Рабочее решение:
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);
}
Ответ 3
Это невозможно.
Понятие "называемый класс" было введено в PHP 5.3. Эта информация не отслеживалась в предыдущих версиях.
Как уродливая работа, вы можете использовать debug_backtrace
для поиска стека вызовов, но это не эквивалентно. Например, в PHP 5.3 использование ClassName::method()
не пересылает статический вызов; вы не можете сказать это с помощью debug_backtrace
. Кроме того, debug_backtrace
является относительно медленным.
Ответ 4
Альтернатива 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)
}
Ответ 5
Решение:
get_class($this);
Однако я не знаю, работает ли это предложение в статических функциях. Попробуйте и скажите мне свои отзывы.
Ответ 6
Этот взлом включает в себя отвратительное использование 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];
}
Ответ 7
Я задал такой вопрос раньше, потому что хотел, чтобы у родителя был метод factory, который был чем-то вроде этого
public static function factory() {
return new __CLASS__;
}
Но он всегда возвращал родительский класс, а не унаследованный.
Мне сказали, что это невозможно без поздней статической привязки. Он был введен в PHP 5.3. Вы можете прочитать документацию.
Ответ 8
Эта функция выполняет ту же работу, но также работает с экземплярами:
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;
}
}
Ответ 9
<?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'