Find what functions were called (from a variable’s perspective)
In your example, the only thing that was «done» to $hello was calls to strtolower(). ucwords and strtoupper were called on anonymous return values, not $hello. profiling with Xdebug + Kcachegrind may produce useful output, however, depending on what exactly it is you’re trying to achieve.
3 Answers 3
There’s not really an easy way to do this, because variables don’t store «state» or «history». Stack traces (where you probably got your inspiration from) are possible because they’re generated from the existing execution stack, which is stored out of necessity to be able to properly unwind chains of function calls.
In addition, your example is trying to trace a function parameter — but that parameter variable is only defined within the scope of the function. Attempting to reference it outside of the function would result in the interpreter not knowing what variable you’re trying to indicate — it’d think you’re looking for a globally-scoped $hello , not the one used as an argument in the function.
There’s no hook in PHP that does exactly what you want, but you can get a call stack with debug_backtrace():
It’s not possible to do exactly what you’re asking for, but perhaps if you gave a bit more context about what you were hoping to do with that function trace, we could give some suggestions?
This question is in a collective: a subcommunity defined by tags with relevant content and experts.
how to scan all usages of a custom function in all my php files?
I have created my own l($text) function in php for a multi lingual website. i use it like this in my documents :
echo '' . l('Title of the page') . '' . l('Some text here. ') . '
';
My question is, with a php script, how can i scan all my .php files to catch all this function usages and list all the arguments used into a mysql table? the goal, of course, is to not forget any sentences in my traduction files. I didn’t find anything on google or here, so if you have any ideas, or need some more information.
2 Answers 2
- read all *.php files with glob()
- then use a regex to pull the strings out (preg_match())
- strings simple mysql insert?
hi andrew, so my regex have to search for content between (‘ and ‘) ? i have to try this, will mark the question as answered if it’s work, thanks ! 🙂
i just finished, your help was usefull ! 🙂
here is my ugly code for those who can be interested. it’s not beautifuly coded, but not made to be loaded 10000 times per day so.
small precision : i do have to escape various cases as : -> CSS : background: url(‘image.jpg’); and -> jQuery : $(this).html(‘bla bla’); so here is why the regex starts with [^a-z(] 🙂
it works very well now! just have to finish later with recording entries in a mysql table and ensure that i can load the script from time to time when there are changes on the site. keep the existing translation, overwrite the existing files etc. no problem with that.
thanks a gain, this website is really helpful ! 🙂
How do I find the list of functions executed to build a page?
I want the list of all functions executed to a certain point in code, somehow like debug_backtrace() but including functions not in the exact thread that leads to where debug_backtrace() is called. e.g :
a(); function a() < b(); c(); d(); >function b() < >function c() < >function d()
3 Answers 3
You could install a tick handler, and on every tick use debug_backtrace to find which function you are in and add it to a list. It will kill performance, but would give the list you desire.
$functions_called=array(); function tick_handler() < $trace=debug_backtrace(); //we're in tick_handler, let's eat that array_shift($trace); //if we're in a function, log it! if (!empty($trace)) < $function=$trace[0]['function']; //log function - lets just remember it was called global $functions_called; $functions_called[$function]=1; >> //install our tick handler. declare(ticks=1); register_tick_function('tick_handler');
Here’s how you’d get the output you specify from this:
function all_trace() < global $functions_called; $output=''; $separator=''; foreach($functions_called as $func=>$dummy) < $output.=$separator.$func.'()'; $separator=', '; >return $output; >
Pretty nasty really — as others suggest, better to use tools like xdebug designed for the job if you can.
need no Xdebug or dbg.so on server, return more detailed message, based on Paul Dixon’s solution:
$v) > return $arr1; > function my_var_export($var, $is_str=false) <$rtn=preg_replace(array('/Array\s+\(/', '/\[(\d+)\] =>(.*)\n/', '/\[([^\d].*)\] => (.*)\n/'), array('array (', '\1 => \'\2\''."\n", '\'\1\' => \'\2\''."\n"), substr(print_r($var, true), 0, -1)); $rtn=strtr($rtn, array("=> 'array ('"=>'=> array (')); $rtn=strtr($rtn, array(")\n\n"=>")\n")); $rtn=strtr($rtn, array("'\n"=>"',\n", ")\n"=>"),\n")); $rtn=preg_replace(array('/\n +/e'), array('strtr(\'\0\', array(\' \'=>\' \'))'), $rtn); $rtn=strtr($rtn, array(" Object',"=>" Object' <-")); if ($is_str) else > function tick_handler() ';var_export($trace);echo '
';echo '
'; //for debug diyism_trace.php $trace=array_values($trace); $GLOBALS['traces.pre']=$tmp; $fun=@$tmp[1]['class'].@$tmp[1]['type'].@$tmp[1]['function']; if (count($trace)>0 && $trace[0]['file'].'/'.$fun!==@$GLOBALS['traces'][count($GLOBALS['traces'])-1]['key']) //filter empty array and rearrange array_values(), because some lines will trigger two tick events per line, for example: 1.last line is "some code;questmark>" 2.error_reporting(. =0; --$i) $trace[$i]['file'].'/'.$fun1), $trace[$i], array('function'=>strtr($fun2, array('tick_handler'=>'CONTINUE')), 'in_function'=>$fun1) ); TRACES_MODE==='FIREPHP'?fb(trace_output($tmp_fb), 'diyism_trace:'.++$GLOBALS['diyism_trace_no']):''; > > > function trace_output($trace) <$trace['in_function']=strtr(@$trace['in_function'], array('require'=>'', 'require_once'=>'', 'include'=>'', 'include_once'=>'')); $trace['args']=$trace['args']?strtr(preg_replace(array('/\n +/'), array(''), preg_replace(array('/\n \d+ => /'), array(''), substr(my_var_export($trace['args'], true), 7, -3))), array("\r"=>'\r', "\n"=>'\n')):''; return $trace['file'].($trace['in_function']?'/'.$trace['in_function'].'()':'').'/'.$trace['line'].': '.$trace['function'].'('.$trace['args'].')'; > function traces_output() <$request_id=base_convert(time().substr(microtime(),2,6).mt_rand(), 10, 36); TRACES_MODE==='TEXTAREA' ?(print ''):''; > register_tick_function('tick_handler'); (TRACES_MODE==='TEXTAREA' || TRACES_MODE==='TMPLOG')?register_shutdown_function('traces_output'):''; ?>
function b() class cls > function d() ?>
/var/www/test/test.php/3: CONTINUE() /var/www/test/test.php/4: a('a',array (0 => 'hello',)) /var/www/test/test.php/a()/8: CONTINUE() /var/www/test/test.php/a()/9: b() /var/www/test/test.php/b()/18: CONTINUE() /var/www/test/test.php/b()/19: d() /var/www/test/test.php/d()/27: CONTINUE() /var/www/test/test.php/b()/19: CONTINUE() /var/www/test/test.php/a()/9: CONTINUE() /var/www/test/test.php/a()/11: c('cccc') /var/www/test/test.php/c()/23: CONTINUE() /var/www/test/test.php/a()/11: CONTINUE() /var/www/test/test.php/a()/13: d() /var/www/test/test.php/d()/27: CONTINUE() /var/www/test/test.php/a()/13: CONTINUE() /var/www/test/test.php/4: CONTINUE() /var/www/test/test.php/6: b() /var/www/test/test.php/b()/18: CONTINUE() /var/www/test/test.php/b()/19: d() /var/www/test/test.php/d()/27: CONTINUE() /var/www/test/test.php/b()/19: CONTINUE() /var/www/test/test.php/6: CONTINUE()
Get name of caller function in PHP?
Xdebug is categorically not just a PHP function, which was the original request. If you want to e.g. use the caller function name in later PHP logic and not install XDebug on production servers, you need a PHP function.
13 Answers 13
See debug_backtrace — this can trace your call stack all the way to the top.
Here’s how you’d get your caller:
$trace = debug_backtrace(); $caller = $trace[1]; echo "Called by "; if (isset($caller['class'])) echo " in ";
It seems to me that this prints the callee function name. Use list(, $caller) = debug_backtrace(false); to get caller, false for performance 😉 (php5.3)
Many solutions seen on the web get the second element of the backtrace array to get the instance caller: can we be so sure about it? Is the second element always the one we are searching for? I thought a __construct() which includes inside another call such as parent::__construct() could shift of another position the real caller (did not tried yet).
I tried checking the order of the callers returned while using a ReflectionClass, and it obviously changes the position of the «real» caller method, which is visible in the user interface, so no assumption on the backtrace position can be made.
array shift will remove first element and return the removed element. The original array will be modified and this should give the required result echo ‘called by ‘.$trace[0][‘function’]
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1][‘function’]; to get caller name with better performmance.
Xdebug provides some nice functions.
callee(); > function callee() < echo sprintf("callee() called @ %s: %s from %s::%s", xdebug_call_file(), xdebug_call_line(), xdebug_call_class(), xdebug_call_function() ); >> $rollDebug = new MyClass(); ?>
callee() called @ /var/www/xd.php: 16 from MyClass::__construct
To install Xdebug on ubuntu the best way is
sudo aptitude install php5-xdebug
You might need to install php5-dev first
sudo aptitude install php5-dev
This is very late but I would like to share the function that will give name of the function from which current function is called.
public function getCallingFunctionName($completeTrace=false) < $trace=debug_backtrace(); if($completeTrace) < $str = ''; foreach($trace as $caller) < $str .= " -- Called by "; if (isset($caller['class'])) $str .= " From Class "; > > else < $caller=$trace[2]; $str = "Called by "; if (isset($caller['class'])) $str .= " From Class "; > return $str; >
I hope this will be useful.
debug_backtrace() supplies details of parameters, function/method calls in the current call stack.
echo debug_backtrace()[1]['function'];
Or optimised (e.g. for non-debug use cases):
echo debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
The first argument prevents to populate unused function arguments, the second limits the trace to two levels (we need the second).
Made this and using this myself
/** * Gets the caller of the function where this function is called from * @param string what to return? (Leave empty to get all, or specify: "class", "function", "line", "class", etc.) - options see: http://php.net/manual/en/function.debug-backtrace.php */ function getCaller($what = NULL) < $trace = debug_backtrace(); $previousCall = $trace[2]; // 0 is this call, 1 is call in previous function, 2 is caller of that function if(isset($what)) < return $previousCall[$what]; >else < return $previousCall; >>
I just wanted to state that flori’s way won’t work as a function because it will always return the called function name instead of the caller, but I don’t have reputation for commenting. I made a very simple function based on flori’s answer that works fine for my case:
EXAMPLE:
function a($authorisedFunctionsList = array("b"))< $ref = new basicFunctions; $caller = $ref->getCallerFunction(); if(in_array($caller,$authorisedFunctionsList)): echo "Welcome!"; return true; else: echo "Unauthorised caller!"; return false; endif; > function b()< $executionContinues = $this->a(); $executionContinues or exit; //Do something else.. >
You can extract this information from the array returned by debug_backtrace
This one worked best for me: var_dump(debug_backtrace());
$caller = next(debug_backtrace())['function'];
// Outputs an easy to read call trace // Credit: https://www.php.net/manual/en/function.debug-backtrace.php#112238 // Gist: https://gist.github.com/UVLabs/692e542d3b53e079d36bc53b4ea20a4b Class MyClass< public function generateCallTrace() < $e = new Exception(); $trace = explode("\n", $e->getTraceAsString()); // reverse array to make steps line up chronologically $trace = array_reverse($trace); array_shift($trace); // remove array_pop($trace); // remove call to this method $length = count($trace); $result = array(); for ($i = 0; $i < $length; $i++) < $result[] = ($i + 1) . ')' . substr($trace[$i], strpos($trace[$i], ' ')); // replace '#someNum' with '$i)', set the right ordering >return "\t" . implode("\n\t", $result); > > // call function where needed to output call trace /** Example output: 1) /var/www/test/test.php(15): SomeClass->__construct() 2) /var/www/test/SomeClass.class.php(36): SomeClass->callSomething() **/```