Is there a compelling reason to not use debug_backtrace for the sole purpose of determining the calling method\'s class, name, and parameter list? Not for debugging purposes
I did another performance test of debug_backtrace
method. To simulate real stacks i defined several classes which call another class methods randomly - with a specified DEPTH:
<?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";
Result:
Performance (10.000 iterations / stack depth 30 / PHP 7.2):
with debug_backtrace - total time: 0.86011600494385s
without: - total time: 0.043321847915649s
using debug_backtrace
makes this code 20x times slower.
debug_backtrace is one of the PHP error handling functions. The manual encourages users to define their own error handling rules, as well as modify the way the errors can be logged. This allows you to change and enhance error reporting to suit your needs. This also implies that the performance hit from using these functions is negligible.
I think what you're doing is just fine.
The correct answer is that it is totally OK. Alan points the non elegance of PHP, so I won't reiterate his points. The reason to (not be afraid to) use debug_backtrace in runtime code is because PHP is mostly a context free language. Each function does not know about, nor can know the caller's "intention" without using magic function names (for instance in a class -- e.g. __toString()) with casting. Lets consider the case where you have a class, in which (in a method), you want to provide access to calling (outer) class's properties. It is truly messy and prone to errors to pass ($this) around through the function API. (Especially when you want to array_map or call_user_func_array.)
e.g.
<?
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);
?>
The debug_backtrace will give you access to the context of class a in the __construct of b. Now you can pass some state variables from the current class without passing $this around or trying to divine it from class names or kludge it by putting it into globals.
For those that are interested in the time, microtime was 2.7E-5 . Fast enough. Just don't put it in a while(1).
You said in a comment
The callee's task is to call the same method that called it on a different computer. It's to avoid having a different API function for every possible source, which reduced the amount of code in the system considerably. Is it still bad
So you want to do the following:
Assuming, of course, that you use HTTP requests to fire off the remote call.
This is a bit of a strange set up. If you are creating the system from scratch then I'd suggest trying to work around it. If you're shoehorning it into a legacy system then I suppose I understand.
I'd suggest that you always be more explicit when you can. Your use of magic stack introspection might save you a little bit of coding, but to another developer your code will be completely baffling. If'dsuggest that you pass the class and function name to the function that was previously doing the reflection. Then there is no ambiguity about what is happening.
I'm thinking about using the debug_backtrace for debugging mysql statements. It's a hell a lot of easier to pinpoint erroneous or slow queries when you have a header like this at the start of each query inside the database logs:
/*Called from /var/www/micimacko.php at line 28*/ SELECT count(*) FROM rofibeka;
The question remains. What about performance/reliability.
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.