PHP Debug_Backtrace in Production Code to Get Information About Calling Method

PHP debug_backtrace in production code to get information about calling method?

It does feel a little dirty, but as has been well documented, opined, and beaten to death elsewhere, PHP isn't a system designed for elegance.

One highly convoluted reason not to use debug_backtrace for application logic is it's possible some future developer working on PHP could decide "it's just a debug function, performance doesn't matter".

If you're interested in a "better" way of doing this, you could probably use PHP's magic constants to pass in the calling method and class name, and then use a ReflectionMethod object to extract any other information you need.

I put better in quotes because, while this would be cleaner and more correct, the overhead of instantiating a Reflection object may be greater than using the debug_backtrace function.

How to get name of calling function/method in PHP?

The debug_backtrace() function is the only way to know this, if you're lazy it's one more reason you should code the GetCallingMethodName() yourself. Fight the laziness! :D

PHP: how do I know the caller of a function?

Not sure why you would ever care about this, but you can figure that out from the debug_backtrace() function.

Determine where a function has been called with PHP

From within the function debug_backtrace() is the only way to determine the caller, unless of course you pass that information as parameter.

Note, that you cannot use default values to do this. E.g.:

function f($param1, $param2, $caller=__FUNCTION__) ...

__FUNCTION__ will be evaluated at parse time, so it'll always have the same value. The function in which scope f is declared.

Find the class name of the calling function in php

One not so good solution is :
use __METHOD__ or __FUNCTION__ or __CLASS__ .
and pass it as parameter to function being called.
http://codepad.org/AVG0Taq7

<?php

class Zebra{
public static function action($source){
print 'I was called from the '.$source.' class'; // How do I get water here?
}
}

class Water{
public static function drink(){
Zebra::action(__CLASS__);
}
}

Water::drink();

?>

Why is debug_backtrace() not including line number sometimes?

Consider following code:

<?
class BtTest
{
public function getTheItem()
{
var_dump( debug_backtrace( false ) );
$bt = debug_backtrace( false );
return $bt[1];
}

public function __call( $methodName, $methodArgs )
{
return $this->getTheItem();
}
}

$o = new BtTest();
$bti = $o->test();

assert( 'array_key_exists("function", $bti)' );
assert( 'array_key_exists("line", $bti)' );
assert( 'array_key_exists("file", $bti)' );

The execution of above example generates following output:

array(3) {
[0]=>
array(6) {
["file"]=>
string(53) "/somewhere/in/the/filesystem/tests/bt-test-so.php"
["line"]=>
int(13)
["function"]=>
string(10) "getTheItem"
["class"]=>
string(6) "BtTest"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[1]=>
array(4) {
["function"]=>
string(6) "__call"
["class"]=>
string(6) "BtTest"
["type"]=>
string(2) "->"
["args"]=>
array(2) {
[0]=>
&string(4) "test"
[1]=>
&array(0) {
}
}
}
[2]=>
array(6) {
["file"]=>
string(53) "/somewhere/in/the/filesystem/tests/bt-test-so.php"
["line"]=>
int(18)
["function"]=>
string(4) "test"
["class"]=>
string(6) "BtTest"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
}
PHP Warning: assert(): Assertion "array_key_exists("line", $bti)" failed in /somewhere/in/the/filesystem/tests/bt-test-so.php on line 21
PHP Warning: assert(): Assertion "array_key_exists("file", $bti)" failed in /somewhere/in/the/filesystem/tests/bt-test-so.php on line 22

The first backtrace item (index 0) says indirectly (through the line and file items) that the getTheItem method was called from the __call method.

The second backtrace item (index 1) says that the __call method was called from somewhere (missing line and file items).

The third backtrace item (index 2) says that the test method was called from the global scope of the script.

The place of the __call method call is probably in some method resolution code somewhere in the php interpreter code. There are two possibilities of fixing it. Either the second item should refer interpreter's source code file and line or the second and the third backtrace items should be merged into one. I personally would prefer the second solution as the interpreter's internals are not interesting for me (this is how they seem to do it in python's traceback), however I understand that sometimes the first solution provides more explicit trace (especially when it's a callback which is called from the internals).

So or so, it seems that the developer(s) responsible for (or at least maintaining) the code of the debug_backtrace function doesn't perceive it as a bug or maybe has no easy way to fix it. It would be ok to fill the line and file items with some place holder values (e.g. <unknown-file> and 0 or even nulls) and emphasize it in the documentation. Unless someone will successfully convince them to do it, you just have to handle the special case in your code.

I wrote above just to share my understanding of the strange behaviour of the function. If someone has a willingness to fight for a slightly better world, here are links to some related bug reports:

  • #39070 debug_backtrace output when call_user_func or error handler involved
  • #24214 debug_backtrace() fails to report __FILE__, __LINE__
  • #24405 debug_backtrace - missing info
  • #38047 "file" and "line" sometimes not set in backtrace from inside error handler
  • #44428 "file" and "line" missing in debug_backtrace() output

The oldest report is from 2003, so you shouldn't count on a fast fix :)

Debugging PHP Code with debug_backtrace

I would install XDebug and hook up the remote debugging to your IDE (e.g PhpStorm or Eclipse), that way you will get nice stack dumps on all errors, plus the ability to breakpoint your code and inspect the stack and all object internals at your leisure.

http://xdebug.org/

You can also use it to profile your application call chains without making any code changes (which sounds more like what you are wanting). By using the profiling options, which generate big log files, you can then load these logs into webgrind and visually inspect who's calling what in nice tree structures.

https://code.google.com/p/webgrind/

The Zend tool chain would also provide this kind of deeper debugging functionality out of the box.

Alternatively install an Application Performance Monitoring agent such as App Dynamics or New Relic for similar code-profiling. This is most useful for remote installations (i.e. production) where debugging isn't an option and profiling is expensive.

Print PHP Call Stack

If you want to generate a backtrace, you are looking for debug_backtrace and/or debug_print_backtrace.


The first one will, for instance, get you an array like this one (quoting the manual) :

array(2) {
[0]=>
array(4) {
["file"] => string(10) "/tmp/a.php"
["line"] => int(10)
["function"] => string(6) "a_test"
["args"]=>
array(1) {
[0] => &string(6) "friend"
}
}
[1]=>
array(4) {
["file"] => string(10) "/tmp/b.php"
["line"] => int(2)
["args"] =>
array(1) {
[0] => string(10) "/tmp/a.php"
}
["function"] => string(12) "include_once"
}
}


They will apparently not flush the I/O buffer, but you can do that yourself, with flush and/or ob_flush.

(see the manual page of the first one to find out why the "and/or" ;-) )



Related Topics



Leave a reply



Submit