如何在PHP中获取调用函数/方法的名称?

294

我知道debug_backtrace函数,但我正在寻找一些可以直接使用的类似于GetCallingMethodName()函数的实现?如果这个函数能够提供方法所在的类(如果确实是一个方法),那就更好了。


3
又一个例子,一个带有更好答案或报告的问题或错误报告被标记为之前但质量较差的帖子的重复。我也必须在行业中解决这个问题。 - John
10个回答

664

最简单的方法是:

echo debug_backtrace()[1]['function'];

如下评论所述,这可以通过传递参数来进一步优化:

  • 省略objectargs索引
  • 限制返回的堆栈帧数
echo debug_backtrace(!DEBUG_BACKTRACE_PROVIDE_OBJECT|DEBUG_BACKTRACE_IGNORE_ARGS,2)[1]['function'];

37
为什么这个答案没有被标为正确答案? - MCMXCII
3
刚刚发现这个。非常有用,但值得注意的是这里可能会涉及到重大的额外开销。我运行了 print_r(debug_backtrace()),但它返回的信息太多了,导致我的浏览器崩溃了。 - Mitya
14
自5.4版本开始,您可以传递第二个参数来限制条目数量。 - igorsantos07
5
如果您的浏览器在尝试打印debug_backtrace()时崩溃,那么可能存在其他更严重的问题。这可能是因为您将大型对象作为参数传递,或者您的调用堆栈非常深,或者您正在使用相当不稳定的浏览器! - Bobby Jack
37
值得稍微优化一下:debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2)[1]['function'] - Eugen Mihailescu
显示剩余11条评论

167

debug_backtrace()函数是唯一的途径来获取这个信息,如果你很懒,那么这就是另外一个理由让你自己编写GetCallingMethodName()函数。 打败懒惰! :D


32
"战胜懒惰!:D" 但是懒惰也有好处: http://www.codinghorror.com/blog/archives/000237.html :}所以,如果有人编写了这样的函数,我真的很感激... :} - Dawid Ohia
24
有如此优秀的“编程答案搜索引擎”Stack Overflow,谁还需要谷歌呢,像您这样的用户就足够了 :}说真的,非常感谢! - Dawid Ohia
14
为了提高性能,请传递这些参数,“debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)”。 - anwerj
1
请参考手册链接:http://php.net/manual/zh/function.debug-backtrace.php - michalzuber
3
这句话的意思是,如果程序员为解决问题而得到报酬,那么尝试不重新发明轮子是完全有效的,这与懒惰无关。 - Radium

69

从 PHP 5.4 版本开始,你可以使用

        $dbt=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
        $caller = isset($dbt[1]['function']) ? $dbt[1]['function'] : null;

这样做不会浪费内存,因为它忽略参数并仅返回最后2个回溯堆栈条目,并且不会生成提示信息,就像其他答案一样。


2
@Krzysztof Trzos 尝试使用 echo $caller; 应该可以解决问题。;-) - Minister
2
这应该是被接受的答案。 - Madhur Bhaiya
我确认它适用于PHP 5.6。 - Yevgeniy Afanasyev
7
自 PHP 7 开始,第二行可以写成 $caller = $dbt[1]['function'] ?? null; - mbomb007
请注意,在现代PHP版本中,可以将 isset($something) ? $something : null 缩短为 $something ?? null。编辑:很抱歉,这已经有人提到了。 - thomasrutter
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2)[1]['function'] ?? null; - But those new buttons though..

38
您还可以使用由 php 异常提供的信息,这是一种优雅的解决方案:
function GetCallingMethodName(){
    $e = new Exception();
    $trace = $e->getTrace();
    //position 0 would be the line that called this function so we ignore it
    $last_call = $trace[1];
    print_r($last_call);
}
function firstCall($a, $b){ theCall($a, $b); }
function theCall($a, $b){ GetCallingMethodName(); }
firstCall('lucia', 'php');
并且您将得到以下结果...(voilà!)
数组
(
    [file] => /home/lufigueroa/Desktop/test.php
    [line] => 12
    [function] => theCall
    [args] => 数组
        (
            [0] => lucia
            [1] => php
        )
)

3
有趣的方法,我有点喜欢,但它为什么比使用debug_backtrace好? - ken
2
请注意,我进行了快速基准测试,看起来这个解决方案比debug_backtrace()慢2倍。然而,我们正在谈论微优化,直到您在代码中不经常使用此方法。 - Strae
1
是的,debug_backtrace 是正确的解决方案,但如果你容易忘记函数的名称,创建一个异常并打印其跟踪可能更容易记住。在我的情况下,我只使用它进行代码调试,我没有因任何原因将其留在生产环境中。如果我想构建一个调试/日志记录系统,我会使用 debug_backtrace。 - Lucia
如果我在“theCall()”函数内调用GetCallingMethodName()函数,那么我期望的是调用者函数“firstCall()”,而不是函数本身。在GetCallingMethodName()函数中设置$last_call = $trace[2];可以解决这个问题。 - Selim Acar

31

对我来说,debug_backtrace 造成了内存限制问题,而我希望在生产环境中使用它来记录和发送错误日志。

相反我找到了这个完美的解决方案!

// Make a new exception at the point you want to trace, and trace it!
$e = new Exception;
var_dump($e->getTraceAsString());

// Outputs the following 
#2 /usr/share/php/PHPUnit/Framework/TestCase.php(626): SeriesHelperTest->setUp()
#3 /usr/share/php/PHPUnit/Framework/TestResult.php(666): PHPUnit_Framework_TestCase->runBare()
#4 /usr/share/php/PHPUnit/Framework/TestCase.php(576): PHPUnit_Framework_TestResult->run(Object(SeriesHelperTest))
#5 /usr/share/php/PHPUnit/Framework/TestSuite.php(757): PHPUnit_Framework_TestCase->run(Object(PHPUnit_Framework_TestResult))
#6 /usr/share/php/PHPUnit/Framework/TestSuite.php(733): PHPUnit_Framework_TestSuite->runTest(Object(SeriesHelperTest), Object(PHPUnit_Framework_TestResult))
#7 /usr/share/php/PHPUnit/TextUI/TestRunner.php(305): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult), false, Array, Array, false)
#8 /usr/share/php/PHPUnit/TextUI/Command.php(188): PHPUnit_TextUI_TestRunner->doRun(Object(PHPUnit_Framework_TestSuite), Array)
#9 /usr/share/php/PHPUnit/TextUI/Command.php(129): PHPUnit_TextUI_Command->run(Array, true)
#10 /usr/bin/phpunit(53): PHPUnit_TextUI_Command::main()
#11 {main}"

不错的发现,这简直是黄金。 - majick
这非常适合我的情况。谢谢。 - Mahendra Jella
不错的解决方案 - Muhammad Omer Aslam

24

我最喜欢的方式,在一行中!

debug_backtrace()[1]['function'];

您可以像这样使用它:

echo 'The calling function: ' . debug_backtrace()[1]['function'];
注意,这仅与在过去一年内发布的PHP版本兼容。但出于安全原因,保持PHP更新始终是一个好主意。

注意,这仅与在过去一年内发布的PHP版本兼容。但出于安全原因,保持PHP更新始终是一个好主意。


1
这种方法已经被提供了无数次,问题在于它只能在PHP 5.4或更高版本上运行。 - Dave Chen
2
谢谢@ialarmedalien :D 关于兼容性,我可能更新得太过激进了吗?我认为人们应该保持他们的PHP(合理)更新。它是免费的,并且使用过时的软件存在安全风险。当您使用2或3年前的PHP版本时,不仅会失去像这样的好功能,还会使您的服务器面临风险。 - gavanon
3
@gavanon的评论表明他不理解PHP和大多数流行工具的维护方式。即使在发布PHP 5.4后的多年里,PHP 5.3仍然会因安全问题而接收更新。仅仅因为使用旧版本的PHP,并不意味着您一定存在不安全的PHP。 - user229044
1
@meagar 任何不支持此方法的PHP版本都低于5.4版本。自2015年以来,这些版本已经没有得到支持或更新。因此,不升级确实存在安全风险。(http://php.net/eol.php) - gavanon

15

我刚刚写了一个名为“get_caller”的版本,希望能有所帮助。我的版本比较简单粗暴。您可以在函数中直接运行 get_caller(),不需要像这样指定:

get_caller(__FUNCTION__);

这是完整的脚本,并附带一个奇特的测试案例:

<?php

/* This function will return the name string of the function that called $function. To return the
    caller of your function, either call get_caller(), or get_caller(__FUNCTION__).
*/
function get_caller($function = NULL, $use_stack = NULL) {
    if ( is_array($use_stack) ) {
        // If a function stack has been provided, used that.
        $stack = $use_stack;
    } else {
        // Otherwise create a fresh one.
        $stack = debug_backtrace();
        echo "\nPrintout of Function Stack: \n\n";
        print_r($stack);
        echo "\n";
    }

    if ($function == NULL) {
        // We need $function to be a function name to retrieve its caller. If it is omitted, then
        // we need to first find what function called get_caller(), and substitute that as the
        // default $function. Remember that invoking get_caller() recursively will add another
        // instance of it to the function stack, so tell get_caller() to use the current stack.
        $function = get_caller(__FUNCTION__, $stack);
    }

    if ( is_string($function) && $function != "" ) {
        // If we are given a function name as a string, go through the function stack and find
        // it's caller.
        for ($i = 0; $i < count($stack); $i++) {
            $curr_function = $stack[$i];
            // Make sure that a caller exists, a function being called within the main script
            // won't have a caller.
            if ( $curr_function["function"] == $function && ($i + 1) < count($stack) ) {
                return $stack[$i + 1]["function"];
            }
        }
    }

    // At this stage, no caller has been found, bummer.
    return "";
}

// TEST CASE

function woman() {
    $caller = get_caller(); // No need for get_caller(__FUNCTION__) here
    if ($caller != "") {
        echo $caller , "() called " , __FUNCTION__ , "(). No surprises there.\n";
    } else {
        echo "no-one called ", __FUNCTION__, "()\n";
    }
}

function man() {
    // Call the woman.
    woman();
}

// Don't keep him waiting
man();

// Try this to see what happens when there is no caller (function called from main script)
//woman();

?>

man()调用woman(),woman()调用get_caller()。由于woman()非常谨慎并没有告诉get_caller()是谁调用了它,所以get_caller()会递归查找调用者。然后它返回调用woman()的人。在浏览器的源代码模式下打印出函数堆栈:

Printout of Function Stack: 

Array
(
    [0] => Array
        (
            [file] => /Users/Aram/Development/Web/php/examples/get_caller.php
            [line] => 46
            [function] => get_caller
            [args] => Array
                (
                )

        )

    [1] => Array
        (
            [file] => /Users/Aram/Development/Web/php/examples/get_caller.php
            [line] => 56
            [function] => woman
            [args] => Array
                (
                )

        )

    [2] => Array
        (
            [file] => /Users/Aram/Development/Web/php/examples/get_caller.php
            [line] => 60
            [function] => man
            [args] => Array
                (
                )

        )

)

man() called woman(). No surprises there.

2
此外,您可以调用 get_caller(someFunctionName) 来获取调用堆栈中任何名称的信息。因此,如果 wife() 调用了 man(),而 man() 又调用了 woman(),则您可以在 woman() 中查找谁在 man() 中调用了它,并挂断电话。 - Aram Kocharyan
很好,谢谢。 - mahdi marjani moghadam

13

我需要一个可以列出调用类/方法的东西(正在处理一个Magento项目)。

虽然debug_backtrace提供了大量有用的信息,但它为Magento安装产生的信息量太多了(超过82,000行!)。因为我只关心调用函数类,所以我想出了这个小解决方案:

$callers = debug_backtrace();
foreach( $callers as $call ) {
    echo "<br>" . $call['class'] . '->' . $call['function'];
}

6
获取父函数名称最简单的方法是:
$caller = next(debug_backtrace())['function'];

我喜欢这个,但在7.4中它会抛出一个通知。PHP Notice: Only variables should be passed by reference - 硬编码数组索引感觉很俗气,但我猜如果你想要一行代码,那就只能这样做了。 - Full Stack Alien

1

我看到的最佳答案是:

list(, $caller) = debug_backtrace(false);

简洁明了

2
debug_backtrace()[1]['function'] 这个不吸引你吗? - AgileTillIDie
4
只有在 PHP 版本大于等于 5.4 才能这样做。 - quickshiftin
[$me, $caller] = debug_backtrace(false); echo $caller['function']; 适用于 PHP >=5.0 和 PHP 7.x - Frank
@fyrye list(, $caller) 是完全有效的 PHP 代码;手册中指出:"list() 构造函数不再能够为空." 这个构造函数并不是空的。关于这个来自2012年的回答,有什么不正确的地方吗?从 PHP 5.3.6 开始,该方法的签名为 debug_backtrace(int $options),不接受 false 参数。 - bryonbean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接