_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 = "
$text
"; $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, '') === false) { foreach (array('script', 'xmp', 'pre') as $tag) { if (substr_count($lower, "<$tag") > substr_count($lower, ""; } } } $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $page = preg_replace('{(?=]*>|$)}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
 tag does, but with 
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; ?>