【问题标题】:PHP debug_backtrace in production code to get information about calling method?生产代码中的 PHP debug_backtrace 以获取有关调用方法的信息?
【发布时间】:2008-12-06 20:10:22
【问题描述】:

是否有令人信服的理由不使用debug_backtrace 来确定调用方法的类、名称和参数列表?不用于调试目的。它的函数名称中有“调试”一词,这让我觉得以这种方式使用它有点脏,但它符合我需要做的事情(可以从许多地方调用的单个函数,并且需要从另一个系统调用调用方法)。它有效,但这仍然是一个坏主意吗?如果有,为什么?

【问题讨论】:

标签: php


【解决方案1】:

确实感觉有点脏,但正如在其他地方有充分的记录、意见和殴打致死的那样,PHP 并不是一个为优雅而设计的系统。

将 debug_backtrace 用于应用程序逻辑的一个非常复杂的原因是,未来从事 PHP 工作的开发人员可能会决定“它只是一个调试函数,性能无关紧要”。

如果您对执行此操作的“更好”方式感兴趣,您可以使用PHP's magic constants 传递调用方法和类名,然后使用ReflectionMethod 对象提取您需要的任何其他信息.

我用引号括起来更好,因为虽然这样会更简洁、更正确,但实例化反射对象的开销可能大于使用 debug_backtrace 函数。

【讨论】:

  • 是的,那时我还不知道 CLASSFUNCTION。或者反射对象。我想如果我再做一次,我会这样做。
  • 要考虑的另一件事是您需要它的频率:在 PHP 代码中维护类似的信息会为每个请求增加大量周期,因此偶尔使用debug_stacktrace 肯定是首选。不过,总的来说,我同意 Konrad 的观点:除了调试/记录/不寻常的需求之外,设计不应该是必需的。
【解决方案2】:

是否有令人信服的理由不使用 debug_backtrace 来确定调用方法的类、名称和参数列表?

是的。关键是,如果您的代码需要如此紧密的耦合,以至于被调用者必须拥有有关其调用者的这些信息,这通常是糟糕设计的标志。因此,如果您觉得有必要使用这些信息,您可能应该重新考虑您的设计。

说白了,被调用者不应该需要这些信息来执行它的任务。当然,例外情况围绕调试、日志记录和更普遍的其他类型的代码自省(但即使在那里,也要当心)。

【讨论】:

  • 被调用者的任务是调用在不同计算机上调用它的相同方法。这是为了避免为每个可能的源使用不同的 API 函数,从而大大减少了系统中的代码量。还是不好?
  • 老实说,我不知道。我看不出有任何不同的处理方式,所以这可能是这种用法的合适案例。
  • @Mawg 是的。错误处理和调试通常非常相似。如果这个函数根本不应该被使用,它为什么会存在?
【解决方案3】:

debug_backtrace 是 PHP 错误处理函数之一。该手册鼓励用户定义自己的错误处理规则,以及修改记录错误的方式。这使您可以更改和增强错误报告以满足您的需要。这也意味着使用这些函数对性能的影响可以忽略不计。

我觉得你做的很好。

【讨论】:

    【解决方案4】:

    你在评论中说

    被调用者的任务是调用在不同计算机上调用它的相同方法。这是为了避免为每个可能的源使用不同的 API 函数,从而大大减少了系统中的代码量。还是很糟糕吗

    所以你想做以下事情:

    当然,假设您使用 HTTP 请求来触发远程调用。

    这有点奇怪。如果您是从头开始创建系统,那么我建议您尝试解决它。如果您将其硬塞进旧系统中,那么我想我明白了。

    我建议您在可能的情况下始终更加明确。您使用魔术堆栈内省可能会为您节省一点编码,但对于另一个开发人员来说,您的代码将完全令人困惑。如果建议您将类和函数名称传递给之前进行反射的函数。这样就不会对正在发生的事情产生歧义了。

    【讨论】:

    • 我第一次写的时候不知道CLASSFUNCTION,否则我可能会知道。我想要一些易于复制/粘贴(并且保持不变)的东西。我真的希望 PHP 有宏(因为它们可以被滥用)。
    【解决方案5】:

    正确答案是完全OK。 Alan 指出了 PHP 的非优雅,所以我不会重复他的观点。在运行时代码中(不要害怕)使用 debug_backtrace 的原因是因为 PHP 主要是一种上下文无关语言。如果不使用带有强制转换的魔术函数名称(例如在类中 - 例如 __toString()),每个函数都不知道,也无法知道调用者的“意图”。让我们考虑一下您有一个类的情况,其中(在一个方法中)您希望提供对调用(外部)类的属性的访问。通过函数 API 传递 ($this) 确实很混乱并且容易出错。 (特别是当你想要 array_map 或 call_user_func_array 时。)

    例如

    <?
    class a {
     public $v = 'xyz';
     function __construct($x,$y)
     {
       $b = new b();
     }
    }
    class b {
     function __construct()
     {
       $start = microtime();
       $x = debug_backtrace();
       $end = microtime();
       echo ($end-$start) . "\n";
       print_r($x);
       $obj = $x[1]['object'];
       print_r($obj);
     }
    }
    $a = new a(1,2);
    ?>
    

    debug_backtrace 将使您能够访问 b 的 __construct 中的类 a 的上下文。现在您可以从当前类传递一些状态变量,而无需传递 $this 或尝试从类名中预测它或通过将其放入全局变量中来混合它。

    对于那些对时间感兴趣的人,microtime 是 2.7E-5 。够快。只是不要把它放在一段时间(1)。

    【讨论】:

      【解决方案6】:

      我对@9​​87654321@ 方法进行了另一次性能测试。为了模拟真实的堆栈,我定义了几个随机调用另一个类方法的类 - 具有指定的深度:

      <?php
      
      const DEPTH = 30;
      const BACKTRACE = false;
      
      class TestClass
      {
          public $a;
          public $b;
      
          public function __construct()
          {
              $foo = new Foo();
              $bar = new Bar();
              $alice = new Alice();
      
              $alice->a = $foo;
              $bar->a = $foo;
      
              $foo->a = $bar;
              $alice->b = $bar;
      
              $foo->b = $alice;
              $bar->b = $alice;
      
              $this->a = $foo;
              $this->b = $bar;
          }
      
          public function method($depth)
          {
              BACKTRACE ? debug_backtrace() : null;
              $obj = mt_rand(0,1) === 1 ? $this->a : $this->b;
      
              if ($depth > DEPTH) {
                  return;
              }
              $obj->method($depth+1);
      
          }
      }
      
      class Foo
      {
          public $a;
          public $b;
      
          public function method($depth)
          {
              BACKTRACE ? debug_backtrace() : null;
      
              $obj = mt_rand(0,1) === 1 ? $this->a : $this->b;
      
              if ($depth > DEPTH) {
                  return;
              }
              $obj->method($depth+1);
          }
      }
      
      class Bar
      {
          public $a;
          public $b;
          public function method($depth)
          {
              BACKTRACE ? debug_backtrace() : null;
              $obj = mt_rand(0,1) === 1 ? $this->a : $this->b;
      
              if ($depth > DEPTH) {
                  return;
              }
              $obj->method($depth+1);
          }
      }
      
      class Alice
      {
          public $a;
          public $b;
      
          public function method($depth)
          {
              BACKTRACE ? debug_backtrace() : null;
      
              $obj = mt_rand(0,1) === 1 ? $this->a : $this->b;
      
              if ($depth > DEPTH) {
                  return;
              }
      
              $obj->method($depth+1);
          }
      }
      
      
      $test = new TestClass();
      
      $startWhole = microtime(true);
      for($i = 0; $i < 10000;$i++) {
          $start = microtime(true);
          $test->method(0);
          $end = microtime(true);
      }
      $endWhole = microtime(true);
      $total = $endWhole - $startWhole;
      
      echo 'total time: ' . $total . "s\n";
      

      结果:

      Performance (10.000 iterations / stack depth 30 / PHP 7.2):
      
      with debug_backtrace - total time: 0.86011600494385s
      without:             - total time: 0.043321847915649s 
      

      使用 debug_backtrace 会使这段代码慢 20 倍。

      【讨论】:

        【解决方案7】:

        我正在考虑使用 debug_backtrace 来调试 mysql 语句。当您在数据库日志中的每个查询开始时都有这样的标头时,查明错误或缓慢的查询要容易得多:

        /*Called from /var/www/micimacko.php at line 28*/ SELECT count(*) FROM rofibeka;
        

        问题仍然存在。性能/可靠性如何。

        【讨论】:

        • 函数 trigger_error() 完全符合您的描述,并将错误保存到 Apache 错误日志中。我使用 trigger_error(mysql_error()) 来保存所有错误以记录或在屏幕上显示它们(取决于我是在生产服务器上还是开发服务器上)。
        猜你喜欢
        • 1970-01-01
        • 2013-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-19
        • 1970-01-01
        相关资源
        最近更新 更多