19

我有一个抽象 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 -ashell 或eval()语句中工作。)

理想情况下,一个解决方案不需要向子类中添加任何代码就可以工作……我能看到的唯一可能的解决方案是将以下代码添加到每个子类中,这显然是一个令人作呕的 hack:

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

编辑:等等,这似乎不起作用。这将是我最后的手段。任何人都可以想到与此解决方案类似的东西,它可以为我提供所需的功能。即,我愿意接受一种解决方案,该解决方案需要我向每个子类添加一个函数或变量,告诉它它的类名是什么。不幸的是,从超类调用似乎self::_get_class_name()调用了父类的实现,即使子类已经覆盖了它。

4

9 回答 9

10

实际上,在执行超类方法时了解实际调用的(子)类通常很有帮助,我不同意想要解决这个问题有什么问题。

例如,我的对象需要知道类名,但它们对这些信息的处理始终是相同的,并且如果我能够获得被调用的类名,则可以将其提取到超类方法中。甚至 PHP 团队也认为这足以包含在 php 5.3 中。

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

于 2011-01-05T17:23:58.740 回答
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);
}
于 2013-03-12T15:07:13.923 回答
3

这是不可能的。

“被称为类”的概念是在 PHP 5.3 中引入的。在以前的版本中未跟踪此信息。

作为一个丑陋的解决方法,您可以使用debug_backtrace查看调用堆栈,但它不是等效的。例如,在 PHP 5.3 中,usingClassName::method()不会转发静态调用;你无法用debug_backtrace. 而且,debug_backtrace速度相对较慢。

于 2010-08-17T00:48:50.167 回答
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)
}
于 2014-08-01T06:22:26.410 回答
1

解决方案是:

get_class($this);

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

于 2010-09-03T15:46:02.637 回答
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];
}
于 2012-06-26T21:26:13.663 回答
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;
    }
}
于 2014-07-04T10:09:17.367 回答
0

我以前问过这样的问题,因为我希望父母有一个类似这样的工厂方法

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

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

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

于 2010-08-17T00:53:36.893 回答
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'
于 2015-12-07T21:30:27.793 回答