You are here

private static function Kint::_getCalleeInfo in Devel 8

Same name and namespace in other branches
  1. 8.2 kint/kint/Kint.class.php \Kint::_getCalleeInfo()

* returns parameter names that the function was passed, as well as any predefined symbols before function * call (modifiers) * *

Parameters

array $trace: * * @return array( $parameters, $modifier, $callee, $previousCaller )

1 call to Kint::_getCalleeInfo()
Kint::dump in kint/kint/Kint.class.php
* Dump information about variables, accepts any number of parameters, supports modifiers: * * clean up any output before kint and place the dump at the top of page: * - Kint::dump() * ***** * expand all nodes on display: * !…

File

kint/kint/Kint.class.php, line 383

Class

Kint

Code

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 false;
  }

  # 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'] . "\7*" . $callee['type'] . "\7*" . $callee['function'];
    }
    else {

      /*if ( $callee['type'] === '->' )*/
      $codePattern = ".*\7*" . $callee['type'] . "\7*" . $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            [\7{(]\n\n            # search for modifiers (group 1)\n            ([-+!@~]*)?\n\n            # spaces\n            \7*\n\n            # check if output is assigned to a variable (group 2) todo: does not detect concat\n            (\n                \\\$[a-z0-9_]+ # variable\n                \7*\\.?=\7*  # assignment\n            )?\n\n            # possibly a namespace symbol\n            \\\\?\n\n\t\t\t# spaces again\n            \7*\n\n            # main call to Kint\n            {$codePattern}\n\n\t\t\t# spaces everywhere\n            \7*\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]);
  $bracket = end($matches[3]);
  $modifiers = $modifiers[0];
  if ($assignment[1] !== -1) {
    $modifiers .= '@';
  }
  $paramsString = preg_replace("[\7+]", ' ', 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] = "\7";
      }
    }
    if ($inString) {
      if ($letter !== $inString || $escaped) {
        $paramsString[$i] = "\7";
      }
    }
    $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("[\7+]", '...', $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,
  );
}