PHP get_called_class() alternative

Deadly 提交于 2019-11-29 03:07:30

In reality it is often helpful to know the actual called (sub)class when executing a superclass method, and I disagree that there's anything wrong with wanting to solve this problem.

Example, my objects need to know the class name, but what they do with that information is always the same and could be extracted into a superclass method IF I was able to get the called class name. Even the PHP team thought this was useful enough to include in php 5.3.

The correct and un-preachy answer, as far as I can tell, is that prior to 5.3, you have to either do something heinous (e.g. backtrace,) or just include duplicate code in each of the subclasses.

Working solution:

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

This is not possible.

The concept of "called class" was introduced in PHP 5.3. This information was not tracked in previous versions.

As an ugly work-around, you could possibly use debug_backtrace to look into the call stack, but it's not equivalent. For instance, in PHP 5.3, using ClassName::method() doesn't forward the static call; you have no way to tell this with debug_backtrace. Also, debug_backtrace is relatively slow.

The PHP/5.2 alternative to late static binding that keeps duplicate code to the minimum while avoiding weird hacks would be to create one-liners on child classes that pass the class name as argument:

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)
}

The solution is:

get_class($this);

However, I don't know if this sentence works in static functions. Give it a try and tell me your feedback.

This hack includes the heinous use of debug_backtrace... not pretty, but it does the job:

<?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];
}

I have asked a question like this before, because I wanted a parent to have a factory method that was something like this

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

But it always returned the parent class, not the inherited one.

I was told that it is not possible without late static binding. It was introduced in PHP 5.3. You can read the documentation.

This function does the same job but works with instances too:

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'
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!