private static function _getCalleeInfo($trace)
{
$previousCaller = array();
$miniTrace = array();
$prevStep = array();
# go from back of trace to find first occurrence of call to Kint or its wrappers
while ($step = array_pop($trace)) {
if (self::_stepIsInternal($step)) {
$previousCaller = $prevStep;
break;
} elseif (isset($step['file'], $step['line'])) {
unset($step['object'], $step['args']);
array_unshift($miniTrace, $step);
}
$prevStep = $step;
}
$callee = $step;
if (!isset($callee['file']) || !is_readable($callee['file'])) {
return array(null, null, $callee, $previousCaller, $miniTrace);
}
# open the file and read it up to the position where the function call expression ended
$file = fopen($callee['file'], 'r');
$line = 0;
$source = '';
while (($row = fgets($file)) !== false) {
if (++$line > $callee['line']) {
break;
}
$source .= $row;
}
fclose($file);
$source = self::_removeAllButCode($source);
if (empty($callee['class'])) {
$codePattern = $callee['function'];
} else {
if ($callee['type'] === '::') {
$codePattern = $callee['class'] . "*" . $callee['type'] . "*" . $callee['function'];
} else {
$codePattern = ".**" . $callee['type'] . "*" . $callee['function'];
}
}
// todo if more than one call in one line - not possible to determine variable names
// todo does not recognize string concat
# get the position of the last call to the function
preg_match_all("\n [\n # beginning of statement\n [{(]\n\n # search for modifiers (group 1)\n ([-+!@~]*)?\n\n # spaces\n *\n\n # check if output is assigned to a variable (group 2) todo: does not detect concat\n (\n \\\$[a-z0-9_]+ # variable\n *\\.?=* # assignment\n )?\n\n # possibly a namespace symbol\n \\\\?\n\n\t\t\t# spaces again\n *\n\n # main call to Kint\n ({$codePattern})\n\n\t\t\t# spaces everywhere\n *\n\n # find the character where kint's opening bracket resides (group 3)\n (\\()\n\n ]ix", $source, $matches, PREG_OFFSET_CAPTURE);
$modifiers = end($matches[1]);
$assignment = end($matches[2]);
$callToKint = end($matches[3]);
$bracket = end($matches[4]);
if (empty($callToKint)) {
# if a wrapper is misconfigured, don't display the whole file as variable name
return array(array(), $modifiers, $callee, $previousCaller, $miniTrace);
}
$modifiers = $modifiers[0];
if ($assignment[1] !== -1) {
$modifiers .= '@';
}
$paramsString = preg_replace("[+]", ' ', substr($source, $bracket[1] + 1));
# we now have a string like this:
# <parameters passed>); <the rest of the last read line>
# remove everything in brackets and quotes, we don't need nested statements nor literal strings which would
# only complicate separating individual arguments
$c = strlen($paramsString);
$inString = $escaped = $openedBracket = $closingBracket = false;
$i = 0;
$inBrackets = 0;
$openedBrackets = array();
while ($i < $c) {
$letter = $paramsString[$i];
if (!$inString) {
if ($letter === '\'' || $letter === '"') {
$inString = $letter;
} elseif ($letter === '(' || $letter === '[') {
$inBrackets++;
$openedBrackets[] = $openedBracket = $letter;
$closingBracket = $openedBracket === '(' ? ')' : ']';
} elseif ($inBrackets && $letter === $closingBracket) {
$inBrackets--;
array_pop($openedBrackets);
$openedBracket = end($openedBrackets);
$closingBracket = $openedBracket === '(' ? ')' : ']';
} elseif (!$inBrackets && $letter === ')') {
$paramsString = substr($paramsString, 0, $i);
break;
}
} elseif ($letter === $inString && !$escaped) {
$inString = false;
}
# replace whatever was inside quotes or brackets with untypeable characters, we don't
# need that info. We'll later replace the whole string with '...'
if ($inBrackets > 0) {
if ($inBrackets > 1 || $letter !== $openedBracket) {
$paramsString[$i] = "";
}
}
if ($inString) {
if ($letter !== $inString || $escaped) {
$paramsString[$i] = "";
}
}
$escaped = !$escaped && $letter === '\\';
$i++;
}
# by now we have an un-nested arguments list, lets make it to an array for processing further
$arguments = explode(',', preg_replace("[+]", '...', $paramsString));
# test each argument whether it was passed literary or was it an expression or a variable name
$parameters = array();
$blacklist = array('null', 'true', 'false', 'array(...)', 'array()', '"..."', '[...]', 'b"..."');
foreach ($arguments as $argument) {
$argument = trim($argument);
if (is_numeric($argument) || in_array(str_replace("'", '"', strtolower($argument)), $blacklist, true)) {
$parameters[] = null;
} else {
$parameters[] = $argument;
}
}
return array($parameters, $modifiers, $callee, $previousCaller, $miniTrace);
}