Index: trunk/kernel/include/debugger.php =================================================================== --- trunk/kernel/include/debugger.php (revision 1290) +++ trunk/kernel/include/debugger.php (revision 1291) @@ -1,821 +1,830 @@ <?php if(!class_exists('Debugger')) { function dbg_ConstOn($const_name) { return defined($const_name)&&constant($const_name); } function dbg_safeDefine($const_name,$const_value) { if(!defined($const_name)) define($const_name,$const_value); } + unset($_REQUEST['debug_host']); // this var messed up whole detection stuff :( + // Detect fact, that this session beeing debugged by Zend Studio foreach($_REQUEST as $rq_name=>$rq_value) { if( substr($rq_name,0,6)=='debug_' ) { define('DBG_ZEND_PRESENT',1); break; } } dbg_safeDefine('DBG_ZEND_PRESENT',0); // set default values for debugger constants $dbg_constMap=Array('DBG_OPTIONS'=>0, 'DBG_USE_HIGHLIGHT'=>1, 'DBG_USE_SHUTDOWN_FUNC'=>DBG_ZEND_PRESENT?0:1, 'DBG_HANDLE_ERRORS'=>DBG_ZEND_PRESENT?0:1, 'DBG_SHOW_MEMORY_USAGE'=>1, + 'DBG_IGNORE_STRICT_ERRORS'=>1, 'DOC_ROOT'=>$_SERVER['DOCUMENT_ROOT'], 'WINDOWS_ROOT'=>'w:'); foreach($dbg_constMap as $dbg_constName=>$dbg_constValue) { dbg_safeDefine($dbg_constName,$dbg_constValue); } // only for IE, in case if no windows php script editor defined if(!defined('WINDOWS_EDITOR')) { $dbg_editor = 0; $dbg_editors[0] = Array('editor' => 'c:\Program Files\UltraEdit\uedit32.exe', 'params' => '%F/%L'); $dbg_editors[1] = Array('editor' => 'c:\Program Files\Zend\ZendStudio-4.0Beta\bin\ZDE.exe', 'params' => '%F'); define('WINDOWS_EDITOR',$dbg_editors[$dbg_editor]['editor'].' '.$dbg_editors[$dbg_editor]['params']); unset($dbg_editors,$dbg_editor); } class Debugger { /** * Debugger data for building report * * @var Array */ var $Data = Array(); var $ProfilerData = Array(); var $RecursionStack = Array(); // prevent recursion when processing debug_backtrace() function results var $TraceNextError=false; var $Options = 0; var $OptionsMap = Array('shutdown_func' => 1, 'error_handler' => 2, 'output_buffer' => 4, 'highlight_output' => 8); var $longErrors=Array(); /** * Amount of memory used by debugger itself * * @var Array * @access private */ var $memoryUsage=Array(); function Debugger() { ini_set('display_errors',dbg_ConstOn('DBG_ZEND_PRESENT')?0:1); $this->memoryUsage['error_handling']=0; // memory amount used by error handler $this->appendRequest(); } function initOptions() { } function mapLongError($msg) { $key=$this->generateID(); $this->longErrors[$key]=$msg; return $key; } function setOption($name,$value) { if( !isset($this->OptionsMap[$name]) ) die('undefined debugger option: ['.$name.']<br>'); if($value) { $this->Options|=$this->OptionsMap[$name]; } else { $this->Options=$this->Options&~$this->OptionsMap[$name]; } } function getOption($name) { if( !isset($this->OptionsMap[$name]) ) die('undefined debugger option: ['.$name.']<br>'); return ($this->Options & $this->OptionsMap[$name]) == $this->OptionsMap[$name]; } /** * Set's flag, that next error that occurs will * be prepended by backtrace results * */ function traceNext() { $this->TraceNextError=true; } 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 = $this->highlightString( print_r($Data['value'], true) ); return addslashes($ret); break; case 'trace': ini_set('memory_limit','500M'); $trace =& $Data['trace']; //return 'sorry'; //return $this->highlightString(print_r($trace,true)); $i = 0; $traceCount = count($trace); $ret = ''; while($i < $traceCount) { $traceRec =& $trace[$i]; $argsID = 'trace_args_'.$dataIndex.'_'.$i; if(isset($traceRec['file'])) { $func_name=isset($traceRec['class'])?$traceRec['class'].$traceRec['type'].$traceRec['function']:$traceRec['function']; $ret .= '<a href="javascript:toggleTraceArgs(\''.$argsID.'\');" title="Show/Hide Function Arguments"><b>Function</b></a>: '.$this->getFileLink($traceRec['file'],$traceRec['line'],$func_name); $ret .= ' in <b>'.basename($traceRec['file']).'</b> on line <b>'.$traceRec['line'].'</b><br>'; } else { $ret .= 'no file information available'; } // ensure parameter value is not longer then 200 symbols $this->processTraceArguments($traceRec['args']); $args = $this->highlightString(print_r($traceRec['args'], true)); $ret .= '<div id="'.$argsID.'" style="display: none;">'.$args.'</div>'; $i++; } 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'].'<br><b>Runtime</b>: '.$runtime.'s'; break; default: return 'incorrect debug data'; break; } } function processTraceArguments(&$traceArgs) { if(!$traceArgs) return ''; foreach ($traceArgs as $argID => $argValue) { if( is_array($argValue) || is_object($argValue) ) { if(is_object($argValue) && !in_array(get_class($argValue),$this->RecursionStack) ) { // object & not in stack - ok array_push($this->RecursionStack, get_class($argValue)); settype($argValue,'array'); $this->processTraceArguments($argValue); array_pop($this->RecursionStack); } elseif(is_object($argValue) && in_array(get_class($argValue),$this->RecursionStack) ) { // object & in stack - recursion $traceArgs[$argID] = '**** RECURSION ***'; } else { // normal array here $this->processTraceArguments($argValue); } } else { $traceArgs[$argID] = $this->cutStringForHTML($traceArgs[$argID]); } } } function cutStringForHTML($string) { if( strlen($string) > 200 ) $string = substr($string,0,50).' ...'; return $string; } /** * Format SQL Query using predefined formatting * and highlighting techniques * * @param string $sql * @return string */ function formatSQL($sql) { $sql = preg_replace('/(\n|\t| )+/is',' ',$sql); $sql = preg_replace('/(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY) /is', "\n\t$1 ",$sql); return $this->highlightString($sql); } function highlightString($string) { if( defined('DBG_USE_HIGHLIGHT')&&DBG_USE_HIGHLIGHT ) { $string = highlight_string('<?php '.$string.'?>', true); return preg_replace('/<\?(.*)php (.*)\?>/s','$2',$string); } else { return $string; } } function getFileLink($file, $lineno = 1, $title = '') { if(!$title) $title = $file; $is_mozilla=strpos(strtolower($_SERVER['HTTP_USER_AGENT']),'firefox')!==false?true:false; if($is_mozilla) { return '<a href="file://'.$this->getLocalFile($file).'">'.$title.'</a>'; } else { 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'); } /** * Change debugger info that was already generated before. * Returns true if html was set. * * @param int $index * @param string $html * @param string $type = {'append','prepend','replace'} * @return bool */ function setHTMLByIndex($index,$html,$type='append') { if( !isset($this->Data[$index]) || $this->Data[$index]['debug_type'] != 'html' ) { return false; } switch ($type) { case 'append': $this->Data[$index]['html'] .= '<br>'.$html; break; case 'prepend': $this->Data[$index]['html'] = $this->Data[$index]['html'].'<br>'.$html; break; case 'replace': $this->Data[$index]['html'] = $html; break; } return true; } /** * Move $debugLineCount lines of input from debug output * end to beginning. * * @param int $debugLineCount */ function moveToBegin($debugLineCount) { $lines = array_splice($this->Data,count($this->Data)-$debugLineCount,$debugLineCount); $this->Data = array_merge($lines,$this->Data); } function appendRequest() { $script = $_SERVER['PATH_TRANSLATED']; $this->appendHTML('ScriptName: <b>'.$this->getFileLink($script,1,basename($script)).'</b> (<b>'.dirname($script).'</b>)'); ob_start(); ?> <table width="100%" border="0" cellspacing="0" cellpadding="4" class="flat_table"> <thead style="font-weight: bold;"> <td width="20">Src</td><td>Name</td><td>Value</td> </thead> <?php foreach($_REQUEST as $key => $value) { if( !is_array($value) && trim($value) == '' ) { $value = '<b class="debug_error">no value</b>'; } else { $value = htmlspecialchars(print_r($value, true)); } $src = isset($_GET[$key]) ? 'GE' : (isset($_POST[$key]) ? 'PO' : (isset($_COOKIE[$key]) ? 'CO' : '?') ); echo '<tr><td>'.$src.'</td><td>'.$key.'</td><td>'.$value.'</td></tr>'; } ?> </table> <?php $this->appendHTML( ob_get_contents() ); ob_end_clean(); } function appendSession() { if( isset($_SESSION)&&$_SESSION ) { $this->appendHTML('PHP Session: [<b>'.ini_get('session.name').'</b>]'); $this->dumpVars($_SESSION); $this->moveToBegin(2); } } 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; + case E_STRICT: + return 'PHP5 Strict'; + break; + default: return ''; break; } } /** * Generates report * */ function printReport($returnResult = false) { if( dbg_ConstOn('DBG_ZEND_PRESENT') ) return; dbg_safeDefine('DBG_RAISE_ON_WARNINGS',0); $this->memoryUsage['debugger_start']=memory_get_usage(); // show php session if any $this->appendSession(); // ensure, that 1st line of debug output always is this one: $this->appendHTML('<a href="javascript:toggleDebugLayer(27);">Hide Debugger</a>'); $this->moveToBegin(1); $i = 0; $lineCount = count($this->Data); ob_start(); ?> <style type="text/css"> .flat_table TD { border: 1px solid buttonface; border-width: 1 1 0 0; } .debug_layer_table { border-collapse: collapse; width: 480px; } .debug_text, .debug_row_even TD, .debug_row_odd TD { color: #000000; font-family: Verdana; font-size: 11px; word-wrap: break-word; } .debug_cell { border: 1px solid #FF0000; padding: 2px; word-wrap: break-word; } .debug_row_even { background-color: #CCCCFF; } .debug_row_odd { background-color: #FFFFCC; } .debug_layer_container { left: 2px; top: 1px; width: 500px; z-index: +1000; position: absolute; overflow: auto; border: 2px solid; padding: 3px; border-top-color: threedlightshadow; border-left-color: threedlightshadow; border-right-color: threeddarkshadow; border-bottom-color: threeddarkshadow; background-color: buttonface; } .debug_layer { padding: 0px; width: 480px; } .debug_error { color: #FF0000; } </style> <div id="debug_layer" class="debug_layer_container" style="display: none;"> <div class="debug_layer"> <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); if($KeyCode==123||$KeyCode==27) // F12 or ESC { toggleDebugLayer($KeyCode); $e.cancelBubble = true; if($e.stopPropagation) $e.stopPropagation(); } } function toggleDebugLayer($KeyCode) { var $isVisible=false; var $DebugLayer = document.getElementById('debug_layer'); if( typeof($DebugLayer) != 'undefined' ) { $isVisible = ($DebugLayer.style.display == 'none')?false:true; if(!$isVisible&&$KeyCode==27) return false; resizeDebugLayer(null); $DebugLayer.style.display = $isVisible?'none':'block'; } } 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(copyText) { if(window.clipboardData) { // IE send-to-clipboard method. window.clipboardData.setData('Text', copyText); } else if (window.netscape) { // You have to sign the code to enable this or allow the action in about:config by changing user_pref("signed.applets.codebase_principal_support", true); netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); // Store support string in an object. var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString); if (!str) return false; str.data=copyText; // Make transferable. var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable); if (!trans) return false; // Specify what datatypes we want to obtain, which is text in this case. trans.addDataFlavor("text/unicode"); trans.setTransferData("text/unicode",str,copyText.length*2); var clipid=Components.interfaces.nsIClipboard; var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(clipid); 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) { if(!document.all) { alert('Only works in IE'); return; } 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 || <?php echo DBG_RAISE_ON_WARNINGS; ?> ) { toggleDebugLayer(); } if( typeof($isFatalError) != 'undefined' && $isFatalError == 1) { document.getElementById('debug_layer').scrollTop = 10000000; } </script> <?php $this->memoryUsage['debugger_finish']=memory_get_usage(); $this->memoryUsage['print_report']=$this->memoryUsage['debugger_finish']-$this->memoryUsage['debugger_start']; $this->memoryUsage['total']=$this->memoryUsage['print_report']+$this->memoryUsage['error_handling']; $this->memoryUsage['application']=memory_get_usage()-$this->memoryUsage['total']; if($returnResult) { $ret = ob_get_contents(); ob_clean(); if( dbg_ConstOn('DBG_SHOW_MEMORY_USAGE') ) $ret.=$this->getMemoryUsageReport(); return $ret; } else { ob_end_flush(); if( dbg_ConstOn('DBG_SHOW_MEMORY_USAGE') ) echo $this->getMemoryUsageReport(); } } /** * Format's memory usage report by debugger * * @return string * @access private */ function getMemoryUsageReport() { - $info=Array('printReport'=>'print_report', + $info=Array('<a href="javascript:self.location.reload()">printReport</a>'=>'print_report', 'saveError'=>'error_handling', 'Total'=>'total', '<u>Application</u>'=>'application'); $ret=Array(); foreach($info as $title => $value_key) { $ret[]=$title.': <b>'.$this->formatSize($this->memoryUsage[$value_key]).'</b>'; } return implode('<br>',$ret); } /** * 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 = '') { $memory_used=Array(); $memory_used['begin']=memory_get_usage(); $errorType = $this->getErrorNameByCode($errno); if(!$errorType) { trigger_error('Unknown error type ['.$errno.']', E_USER_ERROR); return false; } + if( dbg_ConstOn('DBG_IGNORE_STRICT_ERRORS') && defined('E_STRICT') && ($errno == E_STRICT) ) return; + $long_id_pos=strrpos($errstr,'#'); if($long_id_pos!==false) { // replace short message with long one (due triger_error limitations on message size) $long_id=substr($errstr,$long_id_pos+1,strlen($errstr)); $errstr=$this->longErrors[$long_id]; unset($this->longErrors[$long_id]); } if( strpos($errfile,'eval()\'d code') !== false ) { $errstr = '[<b>EVAL</b>, line <b>'.$errline.'</b>]: '.$errstr; $tmpStr = $errfile; $pos = strpos($tmpStr,'('); $errfile = substr($tmpStr,0,$pos); $pos++; $errline = substr($tmpStr,$pos,strpos($tmpStr,')',$pos)-$pos); } if($this->TraceNextError) { $this->appendTrace(); $this->TraceNextError=false; } $this->Data[] = Array('no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error'); $memory_used['end']=memory_get_usage(); $this->memoryUsage['error_handling']+=$memory_used['end']-$memory_used['begin']; if( substr($errorType,0,5) == 'Fatal') { echo '<script language="javascript">var $isFatalError = 1;</script>'; exit; } } function saveToFile($msg) { $fp = fopen($_SERVER['DOCUMENT_ROOT'].'/vb_debug.txt', 'a'); fwrite($fp,$msg."\n"); fclose($fp); } /** * Formats file/memory size in nice way * * @param int $bytes * @return string * @access public */ function formatSize($bytes) { if ($bytes >= 1099511627776) { $return = round($bytes / 1024 / 1024 / 1024 / 1024, 2); $suffix = "TB"; } elseif ($bytes >= 1073741824) { $return = round($bytes / 1024 / 1024 / 1024, 2); $suffix = "GB"; } elseif ($bytes >= 1048576) { $return = round($bytes / 1024 / 1024, 2); $suffix = "MB"; } elseif ($bytes >= 1024) { $return = round($bytes / 1024, 2); $suffix = "KB"; } else { $return = $bytes; $suffix = "Byte"; } $return .= ' '.$suffix; return $return; } } if( !function_exists('memory_get_usage') ) { function memory_get_usage(){ return -1; } } $debugger = new Debugger(); if(dbg_ConstOn('DBG_HANDLE_ERRORS')) set_error_handler( array(&$debugger,'saveError') ); if(dbg_ConstOn('DBG_USE_SHUTDOWN_FUNC')) register_shutdown_function( array(&$debugger,'printReport') ); } ?> \ No newline at end of file Property changes on: trunk/kernel/include/debugger.php ___________________________________________________________________ Modified: cvs2svn:cvs-rev ## -1 +1 ## -1.25 \ No newline at end of property +1.26 \ No newline at end of property