private static function _parseTrace(array $data)
{
$trace = array();
$traceFields = array('file', 'line', 'args', 'class');
$fileFound = false;
# file element must exist in one of the steps
# validate whether a trace was indeed passed
while ($step = array_pop($data)) {
if (!is_array($step) || !isset($step['function'])) {
return false;
}
if (!$fileFound && isset($step['file']) && file_exists($step['file'])) {
$fileFound = true;
}
$valid = false;
foreach ($traceFields as $element) {
if (isset($step[$element])) {
$valid = true;
break;
}
}
if (!$valid) {
return false;
}
if (self::_stepIsInternal($step)) {
$step = array('file' => $step['file'], 'line' => $step['line'], 'function' => '');
array_unshift($trace, $step);
break;
}
if ($step['function'] !== 'spl_autoload_call') {
# meaningless
array_unshift($trace, $step);
}
}
if (!$fileFound) {
return false;
}
$output = array();
foreach ($trace as $step) {
if (isset($step['file'])) {
$file = $step['file'];
if (isset($step['line'])) {
$line = $step['line'];
# include the source of this step
if (self::enabled() === self::MODE_RICH) {
$source = self::_showSource($file, $line);
}
}
}
$function = $step['function'];
if (in_array($function, array('include', 'include_once', 'require', 'require_once'))) {
if (empty($step['args'])) {
# no arguments
$args = array();
} else {
# sanitize the included file path
$args = array('file' => self::shortenPath($step['args'][0]));
}
} elseif (isset($step['args'])) {
if (empty($step['class']) && !function_exists($function)) {
# introspection on closures or language constructs in a stack trace is impossible before PHP 5.3
$params = null;
} else {
try {
if (isset($step['class'])) {
if (method_exists($step['class'], $function)) {
$reflection = new ReflectionMethod($step['class'], $function);
} else {
if (isset($step['type']) && $step['type'] == '::') {
$reflection = new ReflectionMethod($step['class'], '__callStatic');
} else {
$reflection = new ReflectionMethod($step['class'], '__call');
}
}
} else {
$reflection = new ReflectionFunction($function);
}
# get the function parameters
$params = $reflection->getParameters();
} catch (Exception $e) {
# avoid various PHP version incompatibilities
$params = null;
}
}
$args = array();
foreach ($step['args'] as $i => $arg) {
if (isset($params[$i])) {
# assign the argument by the parameter name
$args[$params[$i]->name] = $arg;
} else {
# assign the argument by number
$args['#' . ($i + 1)] = $arg;
}
}
}
if (isset($step['class'])) {
# Class->method() or Class::method()
$function = $step['class'] . $step['type'] . $function;
}
// todo it's possible to parse the object name out from the source!
$output[] = array('function' => $function, 'args' => isset($args) ? $args : null, 'file' => isset($file) ? $file : null, 'line' => isset($line) ? $line : null, 'source' => isset($source) ? $source : null, 'object' => isset($step['object']) ? $step['object'] : null);
unset($function, $args, $file, $line, $source);
}
return $output;
}