<?php class Debugger { /** * Debugger data for building report * * @var Array */ var $Data = Array(); var $ProfilerData = Array(); function dumpVars() { $dumpVars = func_get_args(); foreach($dumpVars as $varValue) { $this->Data[] = Array('value' => $varValue, 'debug_type' => 'var_dump'); } } function prepareHTML($dataIndex) { $Data =& $this->Data[$dataIndex]; if($Data['debug_type'] == 'html') return $Data['html']; switch($Data['debug_type']) { case 'error': $fileLink = $this->getFileLink($Data['file'],$Data['line']); $ret = '<b class="debug_error">'.$this->getErrorNameByCode($Data['no']).'</b>: '.$Data['str']; $ret .= ' in <b>'.$fileLink.'</b> on line <b>'.$Data['line'].'</b>'; return $ret; break; case 'var_dump': $ret = highlight_string('<?php '.print_r($Data['value'], true).'?>', true); $ret = preg_replace('/<\?php (.*)\?>/s','$1',$ret); return addslashes($ret); break; case 'trace': $trace =& $Data['trace']; $i = 0; $traceCount = count($trace); $ret = ''; while($i < $traceCount) { $traceRec =& $trace[$i]; $argsID = 'trace_args_'.$dataIndex.'_'.$i; $ret .= '<a href="javascript:toggleTraceArgs(\''.$argsID.'\');" title="Show/Hide Function Arguments"><b>Function</b></a>: '.$this->getFileLink($traceRec['file'],$traceRec['line'],$traceRec['class'].$traceRec['type'].$traceRec['function']).''; $ret .= ' in <b>'.basename($traceRec['file']).'</b> on line <b>'.$traceRec['line'].'</b><br>'; // ensure parameter value is not longer then 200 symbols foreach ($traceRec['args'] as $argID => $argValue) { if( strlen($argValue) > 200 ) $traceRec['args'][$argID] = substr($argValue,0,50).' ...'; } $args = highlight_string('<?php '.print_r($traceRec['args'], true).'?>', true); $args = preg_replace('/<\?php (.*)\?>/s','$1',$args); $ret .= '<div id="'.$argsID.'" style="display: none;">'.$args.'</div>'; $i++; } /*$ret = highlight_string('<?php '.print_r($trace, true).'?>', true); $ret = preg_replace('/<\?php (.*)\?>/s','$1',$ret);*/ return $ret; break; case 'profiler': $profileKey = $Data['profile_key']; $Data =& $this->ProfilerData[$profileKey]; $runtime = ($Data['ends'] - $Data['begins']); // in seconds return '<b>Name</b>: '.$Data['description'].'. <b>Runtime</b>: '.$runtime.'s'; break; default: return 'incorrect debug data'; break; } } function getFileLink($file, $lineno = 1, $title = '') { if(!$title) $title = $file; return '<a href="javascript:editFile(\''.$this->getLocalFile($file).'\','.$lineno.');" title="'.$file.'">'.$title.'</a>'; } function getLocalFile($remoteFile) { return str_replace(DOC_ROOT, WINDOWS_ROOT, $remoteFile); } function appendTrace() { $trace = debug_backtrace(); array_shift($trace); $this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace'); } function appendHTML($html) { $this->Data[] = Array('html' => $html,'debug_type' => 'html'); } function profileStart($key, $description) { $timeStamp = $this->getMoment(); $this->ProfilerData[$key] = Array('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data), 'description' => $description); $this->Data[] = array('profile_key' => $key, 'debug_type' => 'profiler'); } function profileFinish($key) { $this->ProfilerData[$key]['ends'] = $this->getMoment(); } function getMoment() { list($usec, $sec) = explode(' ', microtime()); return ((float)$usec + (float)$sec); } function generateID() { list($usec, $sec) = explode(" ",microtime()); $id_part_1 = substr($usec, 4, 4); $id_part_2 = mt_rand(1,9); $id_part_3 = substr($sec, 6, 4); $digit_one = substr($id_part_1, 0, 1); if ($digit_one == 0) { $digit_one = mt_rand(1,9); $id_part_1 = ereg_replace("^0","",$id_part_1); $id_part_1=$digit_one.$id_part_1; } return $id_part_1.$id_part_2.$id_part_3; } function getErrorNameByCode($errorCode) { switch($errorCode) { case E_USER_ERROR: return 'Fatal Error'; break; case E_WARNING: case E_USER_WARNING: return 'Warning'; break; case E_NOTICE: case E_USER_NOTICE: return 'Notice'; break; default: return ''; break; } } /** * Generates report * */ function printReport() { $i = 0; $lineCount = count($this->Data); ?> <link rel="stylesheet" type="text/css" href="<?php echo DEBUG_CSS; ?>"> <div id="debug_layer" class="debug_layer_container" style="display: none;"> <div style="padding: 0px;"> <table width="100%" cellpadding="0" cellspacing="1" border="0" class="debug_layer_table"> <?php while ($i < $lineCount) { echo '<tr class="debug_row_'.(($i % 2) ? 'odd' : 'even').'"><td class="debug_cell">'.$this->prepareHTML($i).'</td></tr>'; $i++; } ?> </table> </div> </div> <script language="javascript"> function getEventKeyCode($e) { var $KeyCode = 0; if($e.keyCode) $KeyCode = $e.keyCode; else if($e.which) $KeyCode = $e.which; return $KeyCode; } function keyProcessor($e) { if(!$e) $e = window.event; var $KeyCode = getEventKeyCode($e); //alert(showProps($e)); if($KeyCode == 123 || $KeyCode == 68 && $e.shiftKey) // F12 (for Maxthon) or Ctrl+F2 (for Other Browsers) { toggleDebugLayer(); $e.cancelBubble = true; if($e.stopPropagation) $e.stopPropagation(); } } function toggleDebugLayer() { var $DebugLayer = document.getElementById('debug_layer'); if( typeof($DebugLayer) != 'undefined' ) { resizeDebugLayer(null); $DebugLayer.style.display = ($DebugLayer.style.display == 'none') ? 'block' : 'none'; } } function prepareSizes($Prefix) { var $ret = ''; $ret = eval('document.body.'+$Prefix+'Top')+'; '; $ret += eval('document.body.'+$Prefix+'Left')+'; '; $ret += eval('document.body.'+$Prefix+'Height')+'; '; $ret += eval('document.body.'+$Prefix+'Width')+'; '; return $ret; } function resizeDebugLayer($e) { if(!$e) $e = window.event; var $DebugLayer = document.getElementById('debug_layer'); var $TopMargin = 1; if( typeof($DebugLayer) != 'undefined' ) { $DebugLayer.style.top = parseInt(document.body.offsetTop + document.body.scrollTop) + $TopMargin; $DebugLayer.style.height = document.body.clientHeight - $TopMargin - 5; } window.parent.status = 'OFFSET: '+prepareSizes('offset')+' | SCROLL: '+prepareSizes('scroll')+' | CLIENT: '+prepareSizes('client'); window.parent.status += 'DL Info: '+$DebugLayer.style.top+'; S.AH: '+screen.availHeight; return true; } function SetClipboard($data) { if (window.clipboardData) { window.clipboardData.setData('Text', $data); } else if (window.netscape) { //netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard); if (!clip) return; var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable); if (!trans) return; trans.addDataFlavor('text/unicode'); var str = new Object(); var len = new Object(); var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString); var $copytext=$data; str.data=$copytext; trans.setTransferData("text/unicode",str,$copytext.length*2); var clipid=Components.interfaces.nsIClipboard; if (!clip) return false; clip.setData(trans,null,clipid.kGlobalClipboard); } } function showProps($Obj, $Name) { var $ret = ''; for($Prop in $Obj) { $ret += $Name+'.'+$Prop+' = '+$Obj[$Prop]+"\n"; } return $ret; } function editFile($fileName,$lineNo) { var $editorPath = '<?php echo defined('WINDOWS_EDITOR') ? addslashes(WINDOWS_EDITOR) : '' ?>'; if($editorPath) { var $obj = new ActiveXObject("LaunchinIE.Launch"); $editorPath = $editorPath.replace('%F',$fileName); $editorPath = $editorPath.replace('%L',$lineNo); $obj.LaunchApplication($editorPath); } else { alert('Editor path not defined!'); } } function toggleTraceArgs($ArgsLayerID) { var $ArgsLayer = document.getElementById($ArgsLayerID); $ArgsLayer.style.display = ($ArgsLayer.style.display == 'none') ? 'block' : 'none'; } document.onkeydown = keyProcessor; window.onresize = resizeDebugLayer; window.onscroll = resizeDebugLayer; window.focus(); if( typeof($isFatalError) != 'undefined' && $isFatalError == 1 ) { toggleDebugLayer(); document.getElementById('debug_layer').scrollTop = 10000000; } </script> <?php } /** * User-defined error handler * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @param array $errcontext */ function saveError($errno, $errstr, $errfile = '', $errline = '', $errcontext = '') { //echo '<b>error</b> ['.$errno.'] = ['.$errstr.']<br>'; $errorType = $this->getErrorNameByCode($errno); if(!$errorType) { trigger_error('Unknown error type ['.$errno.']', E_USER_ERROR); return false; } $this->Data[] = Array('no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error'); if( substr($errorType,0,5) == 'Fatal') { echo '<script language="javascript">var $isFatalError = 1;</script>'; exit; } } } $debugger = new Debugger(); $debugger->appendHTML('<a href="javascript:toggleDebugLayer();">Hide Debugger</a>'); set_error_handler( array(&$debugger,'saveError') ); register_shutdown_function( array(&$debugger,'printReport') ); ?>