我有一个抽象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()
会调用父类的实现,即使子类已经覆盖了它。
答案 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'