1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292:
<?php
namespace MvcCore\Debug;
trait Handlers {
public static function Timer ($name = NULL) {
return static::BarDump(
call_user_func(static::$handlers['timer'], $name),
$name,
['backtraceIndex' => 3]
);
}
public static function Dump ($value, $return = FALSE, $exit = FALSE) {
if (static::$originalDebugClass) {
$options = ['bar' => FALSE, 'backtraceIndex' => 1];
if ($exit) $options['lastDump'] = TRUE;
$dumpedValue = static::dumpHandler($value, NULL, $options);
} else {
$dumpedValue = @call_user_func(static::$handlers['dump'], $value, $return);
}
if ($return) return $dumpedValue;
if (static::$debugging) {
echo $dumpedValue;
} else {
static::storeLogRecord($dumpedValue, \MvcCore\IDebug::DEBUG);
}
return $value;
}
public static function BarDump ($value, $title = NULL, $options = []) {
if (static::$originalDebugClass) {
if (!isset($options['backtraceIndex'])) $options['backtraceIndex'] = 1;
$options['bar'] = static::$debugging;
$dumpedValue = static::dumpHandler($value, $title, $options);
} else {
$dumpedValue = @call_user_func_array(static::$handlers['barDump'], func_get_args());
}
if (!static::$debugging)
static::storeLogRecord($dumpedValue, \MvcCore\IDebug::DEBUG);
return $value;
}
public static function Log ($value, $priority = \MvcCore\IDebug::INFO) {
if (static::$originalDebugClass) {
$dumpedValue = static::dumpHandler(
$value, NULL, ['bar' => FALSE, 'backtraceIndex' => 1]
);
return static::storeLogRecord($dumpedValue, $priority);
} else {
return @call_user_func_array(static::$handlers['log'], func_get_args());
}
}
public static function Exception ($exception, $exit = TRUE) {
if (static::$originalDebugClass) {
$dumpedValue = static::dumpHandler(
$exception, NULL, ['bar' => !$exit, 'backtraceIndex' => 1]
);
if (static::$debugging) {
echo $dumpedValue;
} else {
static::storeLogRecord($dumpedValue, \MvcCore\IDebug::EXCEPTION);
}
} else {
@call_user_func_array(static::$handlers['exceptionHandler'], func_get_args());
}
}
public static function ShutdownHandler () {
$error = error_get_last();
if (isset($error['type'])) static::Exception($error);
if (!self::isHtmlResponse() || count(self::$dumps) === 0) return;
list($dumps, $lastDump) = self::formatDebugDumps();
echo str_replace(
['%mvccoreDumps%', '"%mvccoreInitArgs%"'],
[$dumps, ($lastDump ? '!0' : '!1') . ',' . count(self::$dumps)],
file_get_contents(__DIR__.'/debug.html')
);
}
protected static function timerHandler ($name = NULL) {
$now = microtime(TRUE);
if ($name === NULL) return $now - static::$requestBegin;
$difference = round((isset(static::$timers[$name]) ? $now - static::$timers[$name] : 0) * 1000) / 1000;
static::$timers[$name] = $now;
return $difference;
}
protected static function dumpHandler ($value, $title = NULL, $options = []) {
ob_start();
var_dump($value);
$content = preg_replace("#\</small\>\n#", '</small>', ob_get_clean(), 1);
$content = preg_replace("#\<small\>([^\>]*)\>#", '', $content, 1);
$backtraceIndex = isset($options['backtraceIndex']) ? $options['backtraceIndex'] : 2 ;
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $backtraceIndex + 1);
$originalPlace = (object) $backtrace[$backtraceIndex];
$options['file'] = $originalPlace->file;
$options['line'] = $originalPlace->line;
if ($options['bar']) {
if (self::isHtmlResponse()) {
self::$dumps[] = [$content, $title, $options];
} else {
self::sendDumpInAjaxHeader($content, $title, $options);
}
}
return $content;
}
protected static function storeLogRecord ($value, $priority) {
$content = date('[Y-m-d H-i-s]') . "\n" . $value;
$content = preg_replace("#\n(\s)#", "\n\t$1", $content) . "\n";
if (!static::$logDirectoryInitialized) static::initLogDirectory();
$fullPath = static::$LogDirectory . '/' . $priority . '.log';
file_put_contents($fullPath, $content, FILE_APPEND);
return $fullPath;
}
protected static function formatDebugDumps () {
$dumps = '';
$lastDump = FALSE;
$app = static::$app ?: (static::$app = \MvcCore\Application::GetInstance());
$appRoot = $app->GetRequest()->GetAppRoot();
foreach (self::$dumps as $values) {
list($dumpResult, $lastDumpLocal) = self::formatDebugDump($values, $appRoot);
$dumps .= $dumpResult;
if ($lastDumpLocal) $lastDump = $lastDumpLocal;
}
return [$dumps, $lastDump];
}
protected static function formatDebugDump ($dumpRecord, $appRoot = NULL) {
$result = '';
$lastDump = FALSE;
if ($appRoot === NULL) {
$app = static::$app ?: (static::$app = \MvcCore\Application::GetInstance());
$appRoot = $app->GetRequest()->GetAppRoot();
}
$options = $dumpRecord[2];
$result .= '<div class="item">';
if ($dumpRecord[1] !== NULL)
$result .= '<pre class="title">'.$dumpRecord[1].'</pre>';
$file = $options['file'];
$line = $options['line'];
$displayedFile = str_replace('\\', '/', $file);
if (strpos($displayedFile, $appRoot) === 0) {
$displayedFile = substr($displayedFile, strlen($appRoot));
}
$link = '<a class="editor" href="editor://open/?file='
.rawurlencode($file).'&line='.$line.'">'
.$displayedFile.':'.$line
.'</a>';
$dump = & $dumpRecord[0];
$dump = preg_replace("#\n(\s+)\<b\>array\</b\>\s([^\n]+)\n(\s+)(\<i\>\<font )([^\>]+)(\>empty)#m", "<b>array</b> $2 $4$5$6", $dump);
$dump = preg_replace("#\n(\s+)\<b\>array\</b\>\s([^\n]+)\n(\s+)\.\.\.#m", "<b>array</b> $2 ...", $dump);
$dump = preg_replace("#\n(\s+)\<b\>array\</b\>\s([^\n]+)\n#m", "<b>array</b> $2\n", $dump);
$dump = preg_replace("#(\<font color='\#)([^']+)'\>\=>\</font\>\s\n\s+([^\n]+)\n\s+\.\.\.#m", "<font color='#$2'>=></font> $3 ...", $dump);
$dump = preg_replace("#\n\s+(.*)\<b\>object\</b\>([^\n]+)\n#m", "$1<b>object</b> $2\n", $dump);
$result .= '<div class="value">'
.preg_replace("#\[([^\]]*)\]=>([^\n]*)\n(\s*)#", "[$1] => ",
str_replace("<required>","<required>",$link.$dump)
)
.'</div></div>';
if (isset($dumpRecord[2]['lastDump']) && $dumpRecord[2]['lastDump'])
$lastDump = TRUE;
return [$result, $lastDump];
}
protected static function sendDumpInAjaxHeader ($value, $title, $options) {
static $ajaxHeadersIndex = 0;
$app = static::$app ?: (static::$app = \MvcCore\Application::GetInstance());
$response = $app->GetResponse();
list ($dumpStr,) = self::formatDebugDump(
[$value, $title, $options],
$app->GetRequest()->GetAppRoot()
);
$dumpStr64Arr = str_split(base64_encode($dumpStr), 5000);
foreach ($dumpStr64Arr as $key => $base64Item)
$response->SetHeader(
'X-MvcCore-Debug-' . $ajaxHeadersIndex . '-' . $key,
$base64Item
);
$ajaxHeadersIndex += 1;
$response->SetHeader('X-MvcCore-Debug', $ajaxHeadersIndex);
}
protected static function isHtmlResponse () {
$app = static::$app ?: (static::$app = \MvcCore\Application::GetInstance());
$request = $app->GetRequest();
if ($request->IsInternalRequest()) return FALSE;
$response = $app->GetResponse();
return $response->HasHeader('Content-Type') && $response->IsHtmlOutput();
}
}