PHP get_called_class()替代方案

时间:2010-08-17 00:43:01

标签: php oop inheritance php-5.2

我有一个抽象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版中运行。*,因此该函数不可用。

那里有get_called_class()的一些自定义PHP实现,但它们都依赖于debug_backtrack(),解析文件名&行号,并运行正则表达式(因为编码器不知道PHP 5.2有反射)来查找类名。这段代码需要能够用php运行,即。不仅来自.php文件。 (它需要从php -a shell或eval()语句开始工作。)

理想情况下,解决方案可以在不需要将任何代码添加到子类的情况下工作......我能看到的唯一可能的解决方案是将以下代码添加到每个子类中,这显然是一个恶心的黑客攻击:

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

编辑:等等,这似乎不起作用。这将是我的最后一招。任何人都可以想到类似于这个解决方案的东西,它可以让我获得所需的功能。也就是说,我愿意接受一个解决方案,要求我将一个函数或变量添加到每个子类,告诉它它的类名是什么。不幸的是,似乎从超类调用self::_get_class_name()会调用父类的实现,即使子类已经覆盖了它。

9 个答案:

答案 0 :(得分:10)

实际上,在执行超类方法时,知道实际的被调用(子)类通常常常,并且我不同意想要解决这个问题有什么不对。

示例,我的对象需要知道类名,但是他们对该信息的处理方式总是相同的,可以提取到超类方法 IF 我能够获得被调用的类名。甚至PHP团队也认为这很有用,可以包含在php 5.3中。

据我所知,正确和非传播的答案是,在5.3之前,你必须做一些令人发指的事情(例如回溯),或者只是在每个子类中包含重复的代码。

答案 1 :(得分:6)

工作解决方案:

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

答案 2 :(得分:3)

这是不可能的。

PHP 5.3中引入了“被调用类”的概念。在以前的版本中没有跟踪此信息。

作为一个丑陋的解决方法,您可以使用debug_backtrace查看调用堆栈,但它不等同。例如,在PHP 5.3中,使用ClassName::method()不转发静态调用;你无法用debug_backtrace告诉它。此外,debug_backtrace相对较慢。

答案 3 :(得分:2)

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

答案 4 :(得分:1)

解决方案是:

get_class($this);

但是,我不知道这句话是否适用于静态函数。试一试,告诉我你的反馈意见。

答案 5 :(得分:1)

这个hack包含了令人发指的使用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];
}

答案 6 :(得分:0)

之前我曾问过这样的问题,因为我希望父母有一个像这样的工厂方法

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

但是它总是返回父类,而不是继承的类。

有人告诉我,没有后期静态绑定是不可能的。它是在PHP 5.3中引入的。您可以阅读documentation

答案 7 :(得分:0)

此功能执行相同的工作,但也适用于实例:

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

答案 8 :(得分:0)

<?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'