【问题标题】:PHP get_called_class() alternativePHP get_call_class() 替代
【发布时间】:2011-03-30 17:40:09
【问题描述】:

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

【问题讨论】:

  • 老实说,您最好的解决方案是升级到 5.3。
  • 我知道,但不幸的是,这不是一个选择。
  • 我们如何在 java 中做到这一点?

标签: php oop inheritance php-5.2


【解决方案1】:

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

例如,我的对象需要知道类名,但它们对这些信息所做的事情总是相同的,并且可以提取到超类方法中 IF 我能够获得被调用的类名.甚至 PHP 团队也认为这足以包含在 php 5.3 中。

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

【讨论】:

    【解决方案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);
    }
    

    【讨论】:

    • 虽然绝对令人恐惧,但对于提供运行时解决方案的努力 +1。我想如果有人需要拼命支持 php
    • 不幸的是,您的实现不能正常工作。考虑以下示例:class A { public static function foo() { return getCalledClass(); } } class B extends A {} echo B::foo();。预期的结果是“B”(当您使用 get_called_class() 时,这就是您得到的结果),但这里我们得到的是“A”。
    【解决方案3】:

    这是不可能的。

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

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

    【讨论】:

    • 但是,debug_backtrace 将类列为超类而不是子类。它列出了它被调用的文件和行号,所以理论上我可以进入那里并正则表达式该行以找到被调用的类,假设它是用 Bar:: 格式调用的,但这并不是真正的解决方案。而且它不会在文件名被愚蠢地列为“php shell code”的shell上下文中工作
    • @Ken 是的,debug_backtrace 很烂。不幸的是,您剩下的唯一选择是 1) 升级,2) 替换静态方法 other_code_get_class_name 不会这样做,因为从超类中的 other_code 实现调用它会导致始终调用超类的版本) 在每个子类中。
    • 这是可能的。试试 eval ;)
    【解决方案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)
    }
    

    【讨论】:

    • 不完全是我的想法,但这可以工作。毕竟,必须为每个班级写__CLASS__ 并不是一件大事。然后,可以围绕它构建其他逻辑。
    【解决方案5】:

    解决办法是:

    get_class($this);
    

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

    【讨论】:

    • 这基本上就是静态方法的定义:“no $this”。
    【解决方案6】:

    这个 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];
    }
    

    【讨论】:

      【解决方案7】:

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

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

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

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

      【讨论】:

        【解决方案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'
          

          【讨论】:

            猜你喜欢
            • 2013-08-27
            • 2011-03-03
            • 1970-01-01
            • 2021-05-02
            • 2011-10-21
            • 2011-08-26
            • 2011-08-30
            • 2010-11-23
            • 1970-01-01
            相关资源
            最近更新 更多