Подтвердить что ты не робот

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() из суперкласса вызывает реализацию родительского класса, даже если подкласс его переопределил.

4b9b3361

Ответ 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'