You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
5.7 KiB
PHP

<?php
/**
* @internal
*/
class SageTraceStep
{
public $functionName = null;
public $isBlackListed = false;
public $fileLine = null;
public $sourceSnippet = null;
public $arguments = array();
public $argumentNames = array();
/** @var SageVariableData|null */
public $object = null;
public function __construct($step, $stepNumber)
{
$this->fileLine = $this->getFileAndLine($step);
$this->argumentNames = $this->getStepArgumentNames($step);
$this->functionName = $this->getStepFunctionName($step, $this->argumentNames);
if ($this->isStepBlacklisted($step, $stepNumber)) {
$this->isBlackListed = true;
return;
}
// todo it's possible to parse the object name out from the source!!!
$this->object = $this->getObject($step);
$this->sourceSnippet = $this->getSourceSnippet($step);
$this->arguments = $this->getArguments($step, $this->argumentNames);
}
private function isStepBlacklisted($step, $stepNumber)
{
if (! Sage::$maxLevels) {
return false;
}
if (! isset($step['file'])) {
return false;
}
if ($stepNumber < Sage::$minimumTraceStepsToShowFull) {
return false;
}
foreach (Sage::$traceBlacklist as $blacklistedPath) {
if (preg_match($blacklistedPath, $step['file'])) {
return true;
}
}
return false;
}
private function getFileAndLine($step)
{
if (! isset($step['file'])) {
return 'PHP internal call';
}
return SageHelper::ideLink($step['file'], $step['line']);
}
private function getStepArgumentNames($step)
{
if (empty($step['args']) || empty($step['function'])) {
return array();
}
$function = $step['function'];
if (in_array($function, array('include', 'include_once', 'require', 'require_once'))) {
return array('<file>');
}
$reflection = null;
if (isset($step['class'])) {
if (method_exists($step['class'], $function)) {
$reflection = new ReflectionMethod($step['class'], $function);
}
} elseif (function_exists($function)) {
$reflection = new ReflectionFunction($function);
}
$params = $reflection ? $reflection->getParameters() : null;
$names = array();
foreach ($step['args'] as $i => $arg) {
if (isset($params[$i])) {
$names[] = '$' . $params[$i]->name;
} else {
$names[] = '#' . ($i + 1);
}
}
return $names;
}
private function getStepFunctionName($step, $functionNames)
{
if (empty($step['function'])) {
return '';
}
$function = $step['function'];
if ($function && isset($step['class'])) {
$function = $step['class'] . $step['type'] . $function;
}
return $function . '(' . implode(', ', $functionNames) . ')';
}
private function getObject($step)
{
if (! isset($step['object'])) {
return null;
}
return SageParser::process($step['object']);
}
private function getSourceSnippet($step)
{
if (
empty($step['file'])
|| ! isset($step['line'])
|| Sage::enabled() !== Sage::MODE_RICH
|| ! is_readable($step['file'])
) {
return null;
}
// open the file and set the line position
$file = fopen($step['file'], 'r');
$line = $step['line'];
$readingLine = 0;
// Set the reading range
$range = array(
'start' => $line - 7,
'end' => $line + 7,
);
// set the zero-padding amount for line numbers
$format = '% ' . strlen($range['end']) . 'd';
$source = '';
while (($row = fgets($file)) !== false) {
// increment the line number
if (++$readingLine > $range['end']) {
break;
}
if ($readingLine >= $range['start']) {
$row = SageHelper::esc($row);
$row = '<span>' . sprintf($format, $readingLine) . '</span> ' . $row;
if ($readingLine === (int)$line) {
// apply highlighting to this row
$row = '<div class="_sage-highlight">' . $row . '</div>';
} else {
$row = '<div>' . $row . '</div>';
}
$source .= $row;
}
}
fclose($file);
return $source;
}
private function getArguments($step, $argumentNames)
{
$result = array();
foreach ($this->getRawArguments($step) as $k => $variable) {
$name = isset($argumentNames[$k]) ? $argumentNames[$k] : '';
if (SageHelper::isKeyBlacklisted($name)) {
$variable = '*REDACTED*';
}
$parsed = SageParser::process($variable, $argumentNames[$k]);
$parsed->operator = substr($name, 0, 1) === '$' ? '=' : ':';
$result[] = $parsed;
}
return $result;
}
private function getRawArguments($step)
{
if (
! empty($step['args'])
&& in_array($step['function'], array('include', 'include_once', 'require', 'require_once'), true)
) {
// sanitize the included file path
return array(SageHelper::shortenPath($step['args'][0]));
}
return isset($step['args']) ? $step['args'] : array();
}
}