mirror of
https://github.com/Oreolek/ifhub.club.git
synced 2024-07-09 01:44:25 +03:00
309 lines
11 KiB
PHP
309 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Debug_HackerConsole_Main: write debug messages into hidden console.
|
|
* (C) 2005 Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
* See http://www.gnu.org/copyleft/lesser.html
|
|
|
|
* Console may be toggled using Shift+Ctrl+` (tilde) combination.
|
|
*
|
|
* @version 1.x $Id: Main.php 168 2007-01-30 21:12:03Z dk $
|
|
*/
|
|
|
|
class Debug_HackerConsole_Main
|
|
{
|
|
var $_hc_height = "400"; // height of the console (pixels)
|
|
var $_hc_entries = array();
|
|
var $TAB_SIZE = 4;
|
|
|
|
|
|
/**
|
|
* constructor($autoAttachToHtmlOutput=true)
|
|
* Create new console. If $autoAttachToHtmlOutput, output buffering
|
|
* handler is set to automatically attach JavaScript showing code to
|
|
* HTML page.
|
|
*/
|
|
function Debug_HackerConsole_Main($autoAttach=false)
|
|
{
|
|
if ($autoAttach) ob_start(array(&$this, '_obHandler'));
|
|
$GLOBALS['Debug_HackerConsole_Main_LAST'] =& $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* string attachToHtml(string $pageHtml)
|
|
* Attach the console to given HTML page.
|
|
*/
|
|
function attachToHtml($page)
|
|
{
|
|
$js = implode("", file(dirname(__FILE__).'/Js.js'));
|
|
if (get_magic_quotes_runtime()) $js = stripslashes($js);
|
|
$js = str_replace('{HEIGHT}', $this->_hc_height, $js);
|
|
// We MUST use "hackerConsole" instead of "console" because of Safari.
|
|
$code = "window.hackerConsole = window.hackerConsole || window.Debug_HackerConsole_Js && new window.Debug_HackerConsole_Js();\n";
|
|
$code .= "if (window.hackerConsole) setTimeout(function() { with (window.hackerConsole) {\n";
|
|
foreach ($this->_hc_entries as $gid=>$elements) {
|
|
foreach ($elements as $e) {
|
|
if ($e['tip'] === null) {
|
|
$file = str_replace('\\', '/', $e['file']);
|
|
if (isset($_SERVER['DOCUMENT_ROOT'])) {
|
|
// Under IIS DOCUMENT_ROOT may not be available.
|
|
$dr = str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']);
|
|
$file = preg_replace('{^'.preg_quote($dr,'{}').'}is', '~', $file);
|
|
}
|
|
$title = "at {$file} line {$e['line']}" . (!empty($e['function'])? ", {$e['function']}" : "");
|
|
} else {
|
|
$title = $e['tip'];
|
|
}
|
|
$text = $this->toPre($e['text']);
|
|
if (!empty($e['color'])) $text = "<div style=\"color:{$e['color']}\">$text</div>";
|
|
$code .= " out(".$this->_toJs($text).", ".$this->_toJs($title).", ".$this->_toJs($gid).");\n";
|
|
}
|
|
}
|
|
$code .= "}}, 200);";
|
|
$html = '';
|
|
// Dirty close opened tags. This is bad, but better than nothing...
|
|
$lower = strtolower($page);
|
|
if (strpos($lower, '</body>') === false) {
|
|
foreach (array('script', 'xmp', 'pre') as $tag) {
|
|
if (substr_count($lower, "<$tag") > substr_count($lower, "</$tag")) {
|
|
$html .= "</$tag>";
|
|
}
|
|
}
|
|
}
|
|
$html .= "\n";
|
|
$html .= "<!-- ##################### -->\n";
|
|
$html .= "<!-- ### HackerConsole ### -->\n";
|
|
$html .= "<!-- ##################### -->\n";
|
|
$html .= "<script type=\"text/javascript\" language=\"JavaScript\">//<![CDATA[\n{$js}\n{$code}\n//]]></script>\n";
|
|
$page = preg_replace('{(?=</body[^>]*>|$)}si', preg_replace('/([\\\\$])/', '\\\\$1', $html), $page, 1);
|
|
return $page;
|
|
}
|
|
|
|
|
|
/**
|
|
* void out(string $msg, string $group="message", $color=null, $tip=null)
|
|
* Add new message to the console.
|
|
* Messages may be grouped together using $group parameters for better view.
|
|
* By default messages are tipped with caller context (file, line).
|
|
* Contexts generated by call_user_func() are skipped!
|
|
*/
|
|
function out($v, $group="message", $color=null, $tip=null)
|
|
{
|
|
static $dumpCnt;
|
|
if (is_null($dumpCnt)) {
|
|
$dumpCnt = 0;
|
|
}
|
|
|
|
// Have to work only with $obj, NOT $this!
|
|
if (empty($this) || strtolower(get_class($this)) != 'debug_hackerconsole_main') {
|
|
$obj =& $GLOBALS['Debug_HackerConsole_Main_LAST'];
|
|
} else {
|
|
$obj =& $this;
|
|
}
|
|
if (!$obj) return;
|
|
|
|
// Detect caller if needed. Used in tip.
|
|
$s = array();
|
|
if ($tip === null) {
|
|
// Find caller. Use call_user_func to get context of out() calling.
|
|
$s = call_user_func(
|
|
array(&$obj, 'debug_backtrace_smart'),
|
|
'call_user_func.*', // ignore indirect contexts
|
|
true
|
|
);
|
|
}
|
|
|
|
if (is_scalar($v)) $text = "$v\n";
|
|
else $text = Debug_HackerConsole_Main::print_r($v, true);
|
|
$obj->_hc_entries[$group][] = array(
|
|
'file' => isset($s['file'])? $s['file'] : null,
|
|
'line' => isset($s['line'])? $s['line'] : null,
|
|
'function' => isset($s['function'])? $s['function'] : null,
|
|
'text' => '#'.$dumpCnt.' '.$text,
|
|
'color' => $color,
|
|
'tip' => $tip,
|
|
);
|
|
$dumpCnt++;
|
|
}
|
|
|
|
|
|
/**
|
|
* void disable()
|
|
* Disable displaying of the console.
|
|
*/
|
|
function disable()
|
|
{
|
|
// Work only with $obj, NOT $this!
|
|
if (empty($this) || strtolower(get_class($this)) != 'debug_hackerconsole_main') {
|
|
$obj =& $GLOBALS['Debug_HackerConsole_Main_LAST'];
|
|
} else {
|
|
$obj =& $this;
|
|
}
|
|
$obj->disabled = true;
|
|
}
|
|
|
|
/**
|
|
* string toPre($text)
|
|
* Format plaintext like <pre> tag does, but with <br> at the line tails
|
|
* and in line prefixes.
|
|
*/
|
|
function toPre($text, $tabSize=null)
|
|
{
|
|
$text = htmlspecialchars($text);
|
|
// Expand tabulators.
|
|
if ($tabSize === null) {
|
|
if (isset($GLOBALS['Debug_HackerConsole_Main_LAST']))
|
|
$tabSize = $GLOBALS['Debug_HackerConsole_Main_LAST']->TAB_SIZE;
|
|
else
|
|
$tabSize = 4;
|
|
}
|
|
$text = Debug_HackerConsole_Main::expandTabs($text, $tabSize);
|
|
$text = str_replace(' ', ' ', $text);
|
|
$text = nl2br($text);
|
|
return $text;
|
|
}
|
|
|
|
|
|
/**
|
|
* We need manual custom print_r() to use it in OB handlers
|
|
* (original print_r() cannot work inside OB handler).
|
|
*/
|
|
function print_r($obj, $no_print=0, $level=0)
|
|
{
|
|
if ($level < 10) {
|
|
if (is_array($obj)) {
|
|
$type = "Array[".count($obj)."]";
|
|
} elseif (is_object($obj)) {
|
|
$type = "Object";
|
|
} elseif (gettype($obj) == "boolean") {
|
|
$type = $obj? "TRUE" : "FALSE";
|
|
} elseif ($obj === null) {
|
|
$type = "NULL";
|
|
} else {
|
|
$type = preg_replace("/\r?\n/", "\\n", $obj);
|
|
}
|
|
$buf = $type;
|
|
if (is_array($obj) || is_object($obj)) {
|
|
$leftSp = str_repeat(" ", $level+1);
|
|
for (reset($obj); list($k, $v) = each($obj); ) {
|
|
if ($k === "GLOBALS") continue;
|
|
$buf .= "\n{$leftSp}[$k] => ".Debug_HackerConsole_Main::print_r($v, $no_print, $level+1);
|
|
}
|
|
}
|
|
} else {
|
|
$buf = "*RECURSION*";
|
|
}
|
|
$buf = str_replace("\x00", " ", $buf); // PHP5 private methods contain \x00 in names
|
|
if ($no_print) return $buf;
|
|
else echo $buf;
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* string expandTabs($text, $tabSize=4)
|
|
* Correctly convert tabulators to spaces.
|
|
*/
|
|
function expandTabs($text, $tabSize=4)
|
|
{
|
|
$GLOBALS['expandTabs_tabSize'] = $tabSize;
|
|
while (1) {
|
|
$old = $text;
|
|
$text = preg_replace_callback('/^([^\t\r\n]*)\t(\t*)/m', array('Debug_HackerConsole_Main', 'expandTabs_callback'), $text);
|
|
if ($old === $text) return $text;
|
|
}
|
|
}
|
|
|
|
function expandTabs_callback($m)
|
|
{
|
|
$tabSize = $GLOBALS['expandTabs_tabSize'];
|
|
$n =
|
|
intval((strlen($m[1]) + $tabSize) / $tabSize) * $tabSize
|
|
- strlen($m[1])
|
|
+ strlen($m[2]) * $tabSize;
|
|
return $m[1] . str_repeat(' ', $n);
|
|
}
|
|
|
|
|
|
/**
|
|
* Internal methods.
|
|
*/
|
|
|
|
function _obHandler($s)
|
|
{
|
|
return $this->attachToHtml($s);
|
|
}
|
|
|
|
|
|
function _toJs($a)
|
|
{
|
|
$a = addslashes($a);
|
|
$a = str_replace(array("\n", "\r", ">", "<"), array('\n', '\r', "'+'>", "<'+'"), $a);
|
|
return "'$a'";
|
|
}
|
|
|
|
|
|
/**
|
|
* array debug_backtrace_smart($ignoresRe=null, $returnCaller=false)
|
|
*
|
|
* Return stacktrace. Correctly work with call_user_func*
|
|
* (totally skip them correcting caller references).
|
|
* If $returnCaller is true, return only first matched caller,
|
|
* not all stacktrace.
|
|
*
|
|
* @version 2.03
|
|
*/
|
|
function debug_backtrace_smart($ignoresRe=null, $returnCaller=false)
|
|
{
|
|
if (!is_callable($tracer='debug_backtrace')) return array();
|
|
$trace = $tracer();
|
|
|
|
if ($ignoresRe !== null) $ignoresRe = "/^(?>{$ignoresRe})$/six";
|
|
$smart = array();
|
|
$framesSeen = 0;
|
|
for ($i=0, $n=count($trace); $i<$n; $i++) {
|
|
$t = $trace[$i];
|
|
if (!$t) continue;
|
|
|
|
// Next frame.
|
|
$next = isset($trace[$i+1])? $trace[$i+1] : null;
|
|
|
|
// Dummy frame before call_user_func* frames.
|
|
if (!isset($t['file'])) {
|
|
$t['over_function'] = $trace[$i+1]['function'];
|
|
$t = $t + $trace[$i+1];
|
|
$trace[$i+1] = null; // skip call_user_func on next iteration
|
|
}
|
|
|
|
// Skip myself frame.
|
|
if (++$framesSeen < 2) continue;
|
|
|
|
// 'class' and 'function' field of next frame define where
|
|
// this frame function situated. Skip frames for functions
|
|
// situated in ignored places.
|
|
if ($ignoresRe && $next) {
|
|
// Name of function "inside which" frame was generated.
|
|
$frameCaller = (isset($next['class'])? $next['class'].'::' : '') . (isset($next['function'])? $next['function'] : '');
|
|
if (preg_match($ignoresRe, $frameCaller)) continue;
|
|
}
|
|
|
|
// On each iteration we consider ability to add PREVIOUS frame
|
|
// to $smart stack.
|
|
if ($returnCaller) return $t;
|
|
$smart[] = $t;
|
|
}
|
|
return $smart;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Last created console.
|
|
*/
|
|
$GLOBALS['Debug_HackerConsole_Main_LAST'] = null;
|
|
?>
|