View source
<?php
if (defined('KINT_DIR')) {
return;
}
define('KINT_DIR', dirname(__FILE__) . '/');
define('KINT_PHP53', version_compare(PHP_VERSION, '5.3.0') >= 0);
require KINT_DIR . 'config.default.php';
require KINT_DIR . 'inc/kintVariableData.class.php';
require KINT_DIR . 'inc/kintParser.class.php';
require KINT_DIR . 'inc/kintObject.class.php';
require KINT_DIR . 'decorators/rich.php';
require KINT_DIR . 'decorators/plain.php';
if (is_readable(KINT_DIR . 'config.php')) {
require KINT_DIR . 'config.php';
}
if (!empty($GLOBALS['_kint_settings'])) {
Kint::enabled($GLOBALS['_kint_settings']['enabled']);
foreach ($GLOBALS['_kint_settings'] as $key => $val) {
property_exists('Kint', $key) and Kint::${$key} = $val;
}
unset($GLOBALS['_kint_settings'], $key, $val);
}
class Kint {
private static $_enabledMode;
public static $returnOutput;
public static $fileLinkFormat;
public static $displayCalledFrom;
public static $charEncodings;
public static $maxStrLength;
public static $appRootDirs;
public static $maxLevels;
public static $theme;
public static $expandedByDefault;
public static $cliDetection;
public static $cliColors;
const MODE_RICH = 'r';
const MODE_WHITESPACE = 'w';
const MODE_CLI = 'c';
const MODE_PLAIN = 'p';
public static $aliases = array(
'methods' => array(
array(
'kint',
'dump',
),
array(
'kint',
'trace',
),
),
'functions' => array(
'd',
'dd',
'ddd',
's',
'sd',
),
);
private static $_firstRun = true;
public static function enabled($forceMode = null) {
if (isset($forceMode)) {
$before = self::$_enabledMode;
self::$_enabledMode = $forceMode;
return $before;
}
return self::$_enabledMode;
}
public static function trace($trace = null) {
if (!self::enabled()) {
return '';
}
return self::dump(isset($trace) ? $trace : debug_backtrace(true));
}
public static function dump($data = null) {
if (!self::enabled()) {
return '';
}
list($names, $modifiers, $callee, $previousCaller, $miniTrace) = self::_getCalleeInfo(defined('DEBUG_BACKTRACE_IGNORE_ARGS') ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) : debug_backtrace());
$modeOldValue = self::enabled();
$firstRunOldValue = self::$_firstRun;
if (strpos($modifiers, '-') !== false) {
self::$_firstRun = true;
while (ob_get_level()) {
ob_end_clean();
}
}
if (strpos($modifiers, '!') !== false) {
$expandedByDefaultOldValue = self::$expandedByDefault;
self::$expandedByDefault = true;
}
if (strpos($modifiers, '+') !== false) {
$maxLevelsOldValue = self::$maxLevels;
self::$maxLevels = false;
}
if (strpos($modifiers, '@') !== false) {
$returnOldValue = self::$returnOutput;
self::$returnOutput = true;
self::$_firstRun = true;
}
if (strpos($modifiers, '~') !== false) {
self::enabled(self::MODE_WHITESPACE);
}
$mode = self::enabled();
if ($mode === true) {
$mode = PHP_SAPI === 'cli' ? self::MODE_CLI : self::MODE_RICH;
}
self::enabled($mode);
$decorator = self::enabled() === self::MODE_RICH ? 'Kint_Decorators_Rich' : 'Kint_Decorators_Plain';
$output = '';
if (self::$_firstRun) {
$output .= call_user_func(array(
$decorator,
'init',
));
}
$trace = false;
if ($names === array(
null,
) && func_num_args() === 1 && $data === 1) {
$trace = KINT_PHP53 ? debug_backtrace(true) : debug_backtrace();
}
elseif (func_num_args() === 1 && is_array($data)) {
$trace = $data;
}
$trace and $trace = self::_parseTrace($trace);
$output .= call_user_func(array(
$decorator,
'wrapStart',
));
if ($trace) {
$output .= call_user_func(array(
$decorator,
'decorateTrace',
), $trace);
}
else {
$data = func_num_args() === 0 ? array(
"[[no arguments passed]]",
) : func_get_args();
foreach ($data as $k => $argument) {
kintParser::reset();
$output .= call_user_func(array(
$decorator,
'decorate',
), kintParser::factory($argument, isset($names[$k]) ? $names[$k] : ''));
}
}
$output .= call_user_func(array(
$decorator,
'wrapEnd',
), $callee, $miniTrace, $previousCaller);
self::enabled($modeOldValue);
self::$_firstRun = false;
if (strpos($modifiers, '~') !== false) {
self::$_firstRun = $firstRunOldValue;
}
else {
self::enabled($modeOldValue);
}
if (strpos($modifiers, '!') !== false) {
self::$expandedByDefault = $expandedByDefaultOldValue;
}
if (strpos($modifiers, '+') !== false) {
self::$maxLevels = $maxLevelsOldValue;
}
if (strpos($modifiers, '@') !== false) {
self::$returnOutput = $returnOldValue;
self::$_firstRun = $firstRunOldValue;
return $output;
}
if (self::$returnOutput) {
return $output;
}
echo $output;
return '';
}
public static function shortenPath($file) {
$file = str_replace('\\', '/', $file);
$shortenedName = $file;
$replaced = false;
if (is_array(self::$appRootDirs)) {
foreach (self::$appRootDirs as $path => $replaceString) {
if (empty($path)) {
continue;
}
$path = str_replace('\\', '/', $path);
if (strpos($file, $path) === 0) {
$shortenedName = $replaceString . substr($file, strlen($path));
$replaced = true;
break;
}
}
}
if (!$replaced) {
$pathParts = explode('/', str_replace('\\', '/', KINT_DIR));
$fileParts = explode('/', $file);
$i = 0;
foreach ($fileParts as $i => $filePart) {
if (!isset($pathParts[$i]) || $pathParts[$i] !== $filePart) {
break;
}
}
$shortenedName = ($i ? '.../' : '') . implode('/', array_slice($fileParts, $i));
}
return $shortenedName;
}
public static function getIdeLink($file, $line) {
return str_replace(array(
'%f',
'%l',
), array(
$file,
$line,
), self::$fileLinkFormat);
}
private static function _showSource($file, $lineNumber, $padding = 7) {
if (!$file or !is_readable($file)) {
return false;
}
$file = fopen($file, 'r');
$line = 0;
$range = array(
'start' => $lineNumber - $padding,
'end' => $lineNumber + $padding,
);
$format = '% ' . strlen($range['end']) . 'd';
$source = '';
while (($row = fgets($file)) !== false) {
if (++$line > $range['end']) {
break;
}
if ($line >= $range['start']) {
$row = htmlspecialchars($row, ENT_NOQUOTES, 'UTF-8');
$row = '<span>' . sprintf($format, $line) . '</span> ' . $row;
if ($line === $lineNumber) {
$row = '<div class="kint-highlight">' . $row . '</div>';
}
else {
$row = '<div>' . $row . '</div>';
}
$source .= $row;
}
}
fclose($file);
return $source;
}
private static function _getCalleeInfo($trace) {
$previousCaller = array();
$miniTrace = array();
$prevStep = array();
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;
}
$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 {
$codePattern = ".*\7*" . $callee['type'] . "\7*" . $callee['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));
$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;
}
if ($inBrackets > 0) {
if ($inBrackets > 1 || $letter !== $openedBracket) {
$paramsString[$i] = "\7";
}
}
if ($inString) {
if ($letter !== $inString || $escaped) {
$paramsString[$i] = "\7";
}
}
$escaped = !$escaped && $letter === '\\';
$i++;
}
$arguments = explode(',', preg_replace("[\7+]", '...', $paramsString));
$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,
);
}
private static function _removeAllButCode($source) {
$commentTokens = array(
T_COMMENT => true,
T_INLINE_HTML => true,
T_DOC_COMMENT => true,
);
$whiteSpaceTokens = array(
T_WHITESPACE => true,
T_CLOSE_TAG => true,
T_OPEN_TAG => true,
T_OPEN_TAG_WITH_ECHO => true,
);
$cleanedSource = '';
foreach (token_get_all($source) as $token) {
if (is_array($token)) {
if (isset($commentTokens[$token[0]])) {
continue;
}
if (isset($whiteSpaceTokens[$token[0]])) {
$token = "\7";
}
else {
$token = $token[1];
}
}
elseif ($token === ';') {
$token = "\7";
}
$cleanedSource .= $token;
}
return $cleanedSource;
}
private static function _stepIsInternal($step) {
if (isset($step['class'])) {
foreach (self::$aliases['methods'] as $alias) {
if ($alias[0] === strtolower($step['class']) && $alias[1] === strtolower($step['function'])) {
return true;
}
}
return false;
}
else {
return in_array(strtolower($step['function']), self::$aliases['functions'], true);
}
}
private static function _parseTrace(array $data) {
$trace = array();
$traceFields = array(
'file',
'line',
'args',
'class',
);
$fileFound = false;
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') {
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'];
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'])) {
$args = array();
}
else {
$args = array(
'file' => self::shortenPath($step['args'][0]),
);
}
}
elseif (isset($step['args'])) {
if (empty($step['class']) && !function_exists($function)) {
$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);
}
$params = $reflection
->getParameters();
} catch (Exception $e) {
$params = null;
}
}
$args = array();
foreach ($step['args'] as $i => $arg) {
if (isset($params[$i])) {
$args[$params[$i]->name] = $arg;
}
else {
$args['#' . ($i + 1)] = $arg;
}
}
}
if (isset($step['class'])) {
$function = $step['class'] . $step['type'] . $function;
}
$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;
}
}
if (!function_exists('d')) {
function d() {
if (!Kint::enabled()) {
return '';
}
$_ = func_get_args();
return call_user_func_array(array(
'Kint',
'dump',
), $_);
}
}
if (!function_exists('dd')) {
function dd() {
if (!Kint::enabled()) {
return '';
}
echo "<pre>Kint: dd() is being deprecated, please use ddd() instead</pre>\n";
$_ = func_get_args();
call_user_func_array(array(
'Kint',
'dump',
), $_);
die;
}
}
if (!function_exists('ddd')) {
function ddd() {
if (!Kint::enabled()) {
return '';
}
$_ = func_get_args();
call_user_func_array(array(
'Kint',
'dump',
), $_);
die;
}
}
if (!function_exists('s')) {
function s() {
$enabled = Kint::enabled();
if (!$enabled) {
return '';
}
if ($enabled === Kint::MODE_WHITESPACE) {
$restoreMode = Kint::MODE_WHITESPACE;
}
else {
$restoreMode = Kint::enabled(PHP_SAPI === 'cli' ? Kint::MODE_WHITESPACE : Kint::MODE_PLAIN);
}
$params = func_get_args();
$dump = call_user_func_array(array(
'Kint',
'dump',
), $params);
Kint::enabled($restoreMode);
return $dump;
}
}
if (!function_exists('sd')) {
function sd() {
$enabled = Kint::enabled();
if (!$enabled) {
return '';
}
if ($enabled !== Kint::MODE_WHITESPACE) {
Kint::enabled(PHP_SAPI === 'cli' ? Kint::MODE_WHITESPACE : Kint::MODE_PLAIN);
}
$params = func_get_args();
call_user_func_array(array(
'Kint',
'dump',
), $params);
die;
}
}