$dbg_options array instead'); } // check IP before enabling debug mode $ip_match = $this->ipMatch(isset($dbg_options['DBG_IP']) ? $dbg_options['DBG_IP'] : ''); if (!$ip_match) { define('DEBUG_MODE', 0); return ; } // debug is allowed for user, continue initialization $this->InitDebugger(); $this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4', $start); $this->profileStart('script_runtime', 'Script runtime', $start); $this->LastMoment = $start; error_reporting(E_ALL); ini_set('display_errors', $this->constOn('DBG_ZEND_PRESENT') ? 0 : 1); // show errors on screen in case if not in Zend Studio debugging $this->scrollbarWidth = $this->isGecko() ? 22 : 25; // vertical scrollbar width differs in Firefox and other browsers $this->appendRequest(); } /** * Checks, that user IP address is within allowed range * * @param string $ip_list semi-column (by default) separated ip address list * @param string $separator ip address separator (default ";") * * @return bool */ function ipMatch($ip_list, $separator = ';') { $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); foreach ($ip_addresses as $ip_address) { if ($this->netMatch($ip_address, $_SERVER['REMOTE_ADDR'])) { $ip_match = true; break; } } return $ip_match; } /** * Set's default values to constants debugger uses * */ function InitDebugger() { global $dbg_options; unset($dbg_options['DBG_IP']); // Detect fact, that this session beeing debugged by Zend Studio foreach ($_COOKIE as $cookie_name => $cookie_value) { if (substr($cookie_name, 0, 6) == 'debug_') { $this->safeDefine('DBG_ZEND_PRESENT', 1); break; } } $this->safeDefine('DBG_ZEND_PRESENT', 0); // set this constant value to 0 (zero) to debug debugger using Zend Studio // set default values for debugger constants $dbg_constMap = Array ( 'DBG_USE_HIGHLIGHT' => 1, // highlight output same as php code using "highlight_string" function 'DBG_WINDOW_WIDTH' => 700, // set width of debugger window (in pixels) for better viewing large amount of debug data 'DBG_USE_SHUTDOWN_FUNC' => DBG_ZEND_PRESENT ? 0 : 1, // use shutdown function to include debugger code into output 'DBG_HANDLE_ERRORS' => DBG_ZEND_PRESENT ? 0 : 1, // handle all allowed by php (see php manual) errors instead of default handler 'DBG_IGNORE_STRICT_ERRORS' => 1, // ignore PHP5 errors about private/public view modified missing in class declarations 'DBG_DOMVIEWER' => '/temp/domviewer.html', // path to DOMViewer on website 'DOC_ROOT' => str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']) ), // windows hack 'DBG_LOCAL_BASE_PATH' => 'w:', // replace DOC_ROOT in filenames (in errors) using this path ); // only for IE, in case if no windows php script editor defined if (!defined('DBG_EDITOR')) { // $dbg_constMap['DBG_EDITOR'] = 'c:\Program Files\UltraEdit\uedit32.exe %F/%L'; $dbg_constMap['DBG_EDITOR'] = 'c:\Program Files\Zend\ZendStudio-5.2.0\bin\ZDE.exe %F'; } // debugger is initialized before kHTTPQuery, so do jQuery headers check here too if (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { $this->_isAjax = true; } elseif (array_key_exists('ajax', $_GET) && $_GET['ajax'] == 'yes') { $this->_isAjax = true; } // user defined options override debugger defaults $dbg_constMap = $this->array_merge_recursive2($dbg_constMap, $dbg_options); if ($this->_isAjax && array_key_exists('DBG_SKIP_AJAX', $dbg_constMap) && $dbg_constMap['DBG_SKIP_AJAX']) { $dbg_constMap['DBG_SKIP_REPORTING'] = 1; } // when validation configs, don't show sqls for better validation error displaying if (array_key_exists('DBG_VALIDATE_CONFIGS', $dbg_constMap) && $dbg_constMap['DBG_VALIDATE_CONFIGS']) { $dbg_constMap['DBG_SQL_PROFILE'] = 0; } // when showing explain make shure, that debugger window is large enough if (array_key_exists('DBG_SQL_EXPLAIN', $dbg_constMap) && $dbg_constMap['DBG_SQL_EXPLAIN']) { $dbg_constMap['DBG_WINDOW_WIDTH'] = 1000; } foreach ($dbg_constMap as $dbg_constName => $dbg_constValue) { $this->safeDefine($dbg_constName, $dbg_constValue); } } function constOn($const_name) { return defined($const_name) && constant($const_name); } function safeDefine($const_name, $const_value) { if (!defined($const_name)) { define($const_name, $const_value); } } function array_merge_recursive2($paArray1, $paArray2) { if (!is_array($paArray1) or !is_array($paArray2)) { return $paArray2; } foreach ($paArray2 AS $sKey2 => $sValue2) { $paArray1[$sKey2] = isset($paArray1[$sKey2]) ? array_merge_recursive2($paArray1[$sKey2], $sValue2) : $sValue2; } return $paArray1; } function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ($network == $ip) { // comparing two ip addresses directly return true; } $d = strpos($network, '-'); if ($d !== false) { // ip address range specified $from = ip2long(trim(substr($network, 0, $d))); $to = ip2long(trim(substr($network, $d + 1))); $ip = ip2long($ip); return ($ip >= $from && $ip <= $to); } elseif (strpos($network, '/') !== false) { // sigle subnet specified $ip_arr = explode('/', $network); if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) { $ip_arr[0] .= '.0'; // Alternate form 194.1.4/24 } $network_long = ip2long($ip_arr[0]); $x = ip2long($ip_arr[1]); $mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1])); $ip_long = ip2long($ip); return ($ip_long & $mask) == ($network_long & $mask); } return false; } function InitReport() { if (!class_exists('kApplication')) return false; $application =& kApplication::Instance(); // string used to separate debugger records while in file (used in debugger dump filename too) $this->rowSeparator = '@'.(is_object($application->Factory) ? $application->GetSID() : 0).'@'; // $this->rowSeparator = '@'.rand(0,100000).'@'; // include debugger files from this url $reg_exp = '/^'.preg_quote(FULL_PATH, '/').'/'; $kernel_path = preg_replace($reg_exp, '', KERNEL_PATH, 1); $this->baseURL = PROTOCOL.SERVER_NAME.rtrim(BASE_PATH, '/').$kernel_path.'/utility/debugger'; // save debug output in this folder $this->tempFolder = WRITEABLE . '/cache'; } function mapLongError($msg) { $key = $this->generateID(); $this->longErrors[$key] = $msg; return $key; } /** * Appends all passed variable values (wihout variable names) to debug output * */ function dumpVars() { $dump_mode = 'var_dump'; $dumpVars = func_get_args(); if ($dumpVars[count($dumpVars) - 1] === 'STRICT') { $dump_mode = 'strict_var_dump'; array_pop($dumpVars); } foreach ($dumpVars as $varValue) { $this->Data[] = Array('value' => $varValue, 'debug_type' => $dump_mode); } } 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 = ''.$this->getErrorNameByCode($Data['no']).': '.$Data['str']; $ret .= ' in '.$fileLink.' on line '.$Data['line'].''; return $ret; break; case 'var_dump': return $this->highlightString( $this->print_r($Data['value'], true) ); break; case 'strict_var_dump': return $this->highlightString( var_export($Data['value'], true) ); break; case 'trace': ini_set('memory_limit', '500M'); $trace =& $Data['trace']; $i = 0; $traceCount = count($trace); $ret = ''; while ($i < $traceCount) { $traceRec =& $trace[$i]; $argsID = 'trace_args_'.$dataIndex.'_'.$i; $has_args = isset($traceRec['args']); if (isset($traceRec['file'])) { $func_name = isset($traceRec['class']) ? $traceRec['class'].$traceRec['type'].$traceRec['function'] : $traceRec['function']; $args_link = $has_args ? 'Function' : 'Function'; $ret .= $args_link.': '.$this->getFileLink($traceRec['file'], $traceRec['line'], $func_name); $ret .= ' in '.basename($traceRec['file']).' on line '.$traceRec['line'].'
'; } else { $ret .= 'no file information available'; } if ($has_args) { // if parameter value is longer then 200 symbols, then leave only first 50 $args = $this->highlightString($this->print_r($traceRec['args'], true)); $ret .= ''; } $i++; } return $ret; break; case 'profiler': $profileKey = $Data['profile_key']; $Data =& $this->ProfilerData[$profileKey]; $runtime = ($Data['ends'] - $Data['begins']); // in seconds $totals_key = getArrayValue($Data, 'totalsKey'); if ($totals_key) { $total_before = $Data['totalsBefore']; $total = $this->ProfilerTotals[$totals_key]; $div_width = Array(); $total_width = ($this->getWindowWidth() - 10); $div_width['before'] = round(($total_before / $total) * $total_width); $div_width['current'] = round(($runtime / $total) * $total_width); $div_width['left'] = round((($total - $total_before - $runtime) / $total) * $total_width); $ret = 'Name: '.$Data['description'].'
'; $additional = isset($Data['additional']) ? $Data['additional'] : Array (); if (isset($Data['file'])) { array_unshift($additional, Array('name' => 'File', 'value' => $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']).':'.$Data['line']))); } array_unshift($additional, Array('name' => 'Runtime', 'value' => $runtime.'s')); $ret .= '
'; //FF 3.5 needs this! foreach ($additional as $mixed_param) { $ret .= '['.$mixed_param['name'].': '.$mixed_param['value'].'] '; } /*if (isset($Data['file'])) { $ret .= '[Runtime: '.$runtime.'s] [File: '.$this->getFileLink($Data['file'], $Data['line'], basename($Data['file']).':'.$Data['line']).']
'; } else { $ret .= 'Runtime: '.$runtime.'s
'; }*/ $ret .= '
'; $ret .= '
'; $ret .= '
'; $ret .= '
'; return $ret; } else { return 'Name: '.$Data['description'].'
Runtime: '.$runtime.'s'; } break; default: return 'incorrect debug data'; break; } } function getWindowWidth() { return DBG_WINDOW_WIDTH - $this->scrollbarWidth - 8; } /** * Tells debugger to skip objects that are heavy in plan of memory usage while printing debug_backtrace results * * @param Object $object * @return bool */ function IsBigObject(&$object) { $skip_classes = Array( defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication', 'kFactory', 'kUnitConfigReader', 'NParser', ); foreach ($skip_classes as $class_name) { if (strtolower(get_class($object)) == strtolower($class_name)) { return true; } } return false; } /** * Advanced version of print_r (for debugger only). Don't print objects recursively * * @param Array $array * @param bool $return_output return output or print it out * @param int $tab_count offset in tabs * @return string */ function print_r(&$array, $return_output = false, $tab_count = -1) { static $first_line = true; // not an array at all if (!is_array($array)) { switch (gettype($array)) { case 'NULL': return 'NULL'."\n"; break; case 'object': return $this->processObject($array, $tab_count); break; default: // number or string if (strlen($array) > 200) { $array = substr($array, 0, 50).' ...'; } return $array."\n"; break; } } $output = ''; $tab_count++; $output .= "Array\n".str_repeat(' ', $tab_count)."(\n"; $tab_count++; $tabsign = $tab_count ? str_repeat(' ', $tab_count) : ''; $array_keys = array_keys($array); foreach ($array_keys as $key) { switch (gettype($array[$key])) { case 'array': $output .= $tabsign.'['.$key.'] = '.$this->print_r($array[$key], true, $tab_count); break; case 'boolean': $output .= $tabsign.'['.$key.'] = '.($array[$key] ? 'true' : 'false')."\n"; break; case 'integer': case 'double': case 'string': if (strlen($array[$key]) > 200) { $array[$key] = substr($array[$key], 0, 50).' ...'; } $output .= $tabsign.'['.$key.'] = '.$array[$key]."\n"; break; case 'NULL': $output .= $tabsign.'['.$key."] = NULL\n"; break; case 'object': $output .= $tabsign.'['.$key."] = "; $output .= "Object (".get_class($array[$key]).") = \n".str_repeat(' ', $tab_count + 1)."(\n"; $output .= $this->processObject($array[$key], $tab_count + 2); $output .= str_repeat(' ', $tab_count + 1).")\n"; break; default: $output .= $tabsign.'['.$key.'] unknown = '.gettype($array[$key])."\n"; break; } } $tab_count--; $output .= str_repeat(' ', $tab_count).")\n"; if ($first_line) { $first_line = false; $output .= "\n"; } $tab_count--; if ($return_output) { return $output; } else { echo $output; } return true; } function processObject(&$object, $tab_count) { $object_class = get_class($object); if (!in_array($object_class, $this->RecursionStack)) { if ($this->IsBigObject($object)) { return 'SKIPPED (class: '.$object_class.")\n"; } $attribute_names = get_class_vars($object_class); if (!$attribute_names) { return "NO_ATTRIBUTES\n"; } else { $output = ''; array_push($this->RecursionStack, $object_class); $tabsign = $tab_count ? str_repeat(' ', $tab_count) : ''; foreach ($attribute_names as $attribute_name => $attribute_value) { if (is_object($object->$attribute_name)) { // it is object $output .= $tabsign.'['.$attribute_name.'] = '.$this->processObject($object->$attribute_name, $tab_count + 1); } else { $output .= $tabsign.'['.$attribute_name.'] = '.$this->print_r($object->$attribute_name, true, $tab_count); } } array_pop($this->RecursionStack); return $output; } } else { // object [in recursion stack] return '*** RECURSION *** (class: '.$object_class.")\n"; } } /** * Format SQL Query using predefined formatting * and highlighting techniques * * @param string $sql * @return string */ function formatSQL($sql) { $sql = trim( preg_replace('/(\n|\t| )+/is', ' ', $sql) ); // whitespace in the beginning of the regex is to avoid splitting inside words, for exmaple "FROM int_ConfigurationValues" into "FROM intConfiguration\n\tValues" $formatted_sql = preg_replace('/\s(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY)\s/is', "\n\t$1 ", ' ' . $sql); $formatted_sql = $this->highlightString($formatted_sql); if (defined('DBG_SQL_EXPLAIN') && DBG_SQL_EXPLAIN) { if (substr($sql, 0, 6) == 'SELECT') { $formatted_sql .= '
' . 'Explain:

'; $explain_result = $this->Application->Conn->Query('EXPLAIN ' . $sql, null, true); $explain_table = ''; foreach ($explain_result as $explain_row) { if (!$explain_table) { // first row -> draw header $explain_table .= '' . implode('', array_keys($explain_row)) . ''; } $explain_table .= '' . implode('', $explain_row) . ''; } $formatted_sql .= '' . $explain_table . '
'; } } return $formatted_sql; } function highlightString($string) { if (!(defined('DBG_USE_HIGHLIGHT') && DBG_USE_HIGHLIGHT) || $this->_compileError) { return nl2br($string); } $string = str_replace( Array('\\', '/') , Array('_no_match_string_', '_n_m_s_'), $string); $this->_compileError = true; // next line is possible cause of compile error $string = highlight_string('', true); $this->_compileError = false; $string = str_replace( Array('_no_match_string_', '_n_m_s_'), Array('\\', '/'), $string); if (strlen($string) >= 65536) { // preg_replace will fail, when string is longer, then 65KB return str_replace(Array ('<?php ', '?>'), '', $string); } return preg_replace('/<\?(.*)php (.*)\?>/Us', '\\2', $string); } /** * Determine by php type of browser used to show debugger * * @return bool */ function isGecko() { // we need isset because we may run scripts from shell with no user_agent at all return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false; } /** * Returns link for editing php file (from error) in external editor * * @param string $file filename with path from root folder * @param int $lineno line number in file where error is found * @param string $title text to show on file edit link * @return string */ function getFileLink($file, $lineno = 1, $title = '') { if (!$title) { $title = str_replace('/', '\\', $this->getLocalFile($file)); } if ($this->isGecko()) { return ''.$title.''; } else { return ''.$title.''; } } /** * Converts filepath on server to filepath in mapped DocumentRoot on developer pc * * @param string $remoteFile * @return string */ function getLocalFile($remoteFile) { return preg_replace('/^'.preg_quote(DOC_ROOT, '/').'/', DBG_LOCAL_BASE_PATH, $remoteFile, 1); } /** * Appends call trace till this method call * */ function appendTrace() { $trace = debug_backtrace(); array_shift($trace); $this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace'); } function appendMemoryUsage($msg, $used = null) { if (!isset($used)) { $used = round(memory_get_usage() / 1024); } $this->appendHTML('Memory usage '.$msg.' '.$used.'Kb'); } /** * Appends HTML code whithout transformations * * @param string $html */ 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'] .= '
'.$html; break; case 'prepend': $this->Data[$index]['html'] = $this->Data[$index]['html'].'
'.$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 moveAfterRow($new_row, $debugLineCount) { $lines = array_splice($this->Data,count($this->Data)-$debugLineCount,$debugLineCount); $rows_before = array_splice($this->Data,0,$new_row,$lines); $this->Data = array_merge($rows_before,$this->Data); } function appendRequest() { if (isset($_SERVER['SCRIPT_FILENAME'])) { $script = $_SERVER['SCRIPT_FILENAME']; } else { $script = $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF']; } $this->appendHTML('ScriptName: '.$this->getFileLink($script, 1, basename($script)).' ('.dirname($script).')'); if ($this->_isAjax) { $this->appendHTML('RequestURI: '.$_SERVER['REQUEST_URI'].' (QS Length:'.strlen($_SERVER['QUERY_STRING']).')'); } $tools_html = '
' . $this->_getDomViewerHTML() . ' ' . $this->_getToolsHTML() . '
'; $this->appendHTML($tools_html); ob_start(); ?> $value) { if(!is_array($value) && trim($value) == '') { $value = 'no value'; } else { $value = htmlspecialchars($this->print_r($value, true)); } $in_cookie = isset($_COOKIE[$key]); $src = isset($_GET[$key]) && !$in_cookie ? 'GE' : (isset($_POST[$key]) && !$in_cookie ? 'PO' : ($in_cookie ? 'CO' : '?') ); echo ''; } ?>
SrcNameValue
'.$src.''.$key.''.$value.'
appendHTML(ob_get_contents()); ob_end_clean(); } /** * Appends php session content to debugger output * */ function appendSession() { if (isset($_SESSION) && $_SESSION) { $this->appendHTML('PHP Session: ['.ini_get('session.name').']'); $this->dumpVars($_SESSION); $this->moveToBegin(2); } } function profileStart($key, $description = null, $timeStamp = null) { if (!isset($timeStamp)) { $timeStamp = $this->getMoment(); } $this->ProfilerData[$key] = Array('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data)); if (isset($description)) { $this->ProfilerData[$key]['description'] = $description; } if (substr($key, 0, 4) == 'sql_') { // append place from what was called $trace_results = debug_backtrace(); $trace_count = count($trace_results); $i = 0; while ($i < $trace_count) { $trace_file = basename($trace_results[$i]['file']); if ($trace_file != 'db_connection.php' && $trace_file != 'adodb.inc.php') { break; } $i++; } $this->ProfilerData[$key]['file'] = $trace_results[$i]['file']; $this->ProfilerData[$key]['line'] = $trace_results[$i]['line']; unset($trace_results); } $this->Data[] = Array('profile_key' => $key, 'debug_type' => 'profiler'); } function profileFinish($key, $description = null, $timeStamp = null) { if (!isset($timeStamp)) { $timeStamp = $this->getMoment(); } $this->ProfilerData[$key]['ends'] = $timeStamp; if (isset($description)) { $this->ProfilerData[$key]['description'] = $description; } if (substr($key, 0, 4) == 'sql_') { $func_arguments = func_get_args(); $rows_affected = $func_arguments[3]; $additional = Array (); if ($rows_affected > 0) { $additional[] = Array ('name' => 'Affected Rows', 'value' => $rows_affected); if (isset($func_arguments[4])) { if (strlen($func_arguments[4]) > 200) { $func_arguments[4] = substr($func_arguments[4], 0, 50) . ' ...'; } $additional[] = Array ('name' => 'Result', 'value' => $func_arguments[4]); } } $additional[] = Array ('name' => 'Query Number', 'value' => $func_arguments[5]); $this->ProfilerData[$key]['additional'] =& $additional; } } function profilerAddTotal($total_key, $key = null, $value = null) { if (!isset($this->ProfilerTotals[$total_key])) { $this->ProfilerTotals[$total_key] = 0; $this->ProfilerTotalCount[$total_key] = 0; } if (!isset($value)) { $value = $this->ProfilerData[$key]['ends'] - $this->ProfilerData[$key]['begins']; } if (isset($key)) { $this->ProfilerData[$key]['totalsKey'] = $total_key; $this->ProfilerData[$key]['totalsBefore'] = $this->ProfilerTotals[$total_key]; } $this->ProfilerTotals[$total_key] += $value; $this->ProfilerTotalCount[$total_key]++; } function getMoment() { list($usec, $sec) = explode(' ', microtime()); return ((float)$usec + (float)$sec); } function appendTimestamp($message) { global $start; $time = $this->getMoment(); $from_last = $time - $this->LastMoment; $from_start = $time - $start; $this->appendHTML(sprintf("%s %.5f from last %.5f from start", $message, $from_last, $from_start)); $this->LastMoment = $time; } 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 = preg_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($error_code) { $error_map = Array( 'Fatal Error' => Array(E_USER_ERROR), 'Warning' => Array(E_WARNING, E_USER_WARNING), 'Notice' => Array(E_NOTICE, E_USER_NOTICE), ); if (defined('E_STRICT')) { // since PHP 5 $error_map['PHP5 Strict'] = Array(E_STRICT); } if (defined('E_RECOVERABLE_ERROR')) { // since PHP 5.2 $error_map['Fatal Error (recoverable)'] = Array(E_RECOVERABLE_ERROR); } if (defined('E_DEPRECATED')) { // since PHP 5.3 $error_map['PHP5 Depricated'] = Array(E_DEPRECATED, E_USER_DEPRECATED); } foreach ($error_map as $error_name => $error_codes) { if (in_array($error_code, $error_codes)) { return $error_name; } } return ''; } /** * Returns profile total key (check against unexisting key too) * * @param string $key * @return int */ function getProfilerTotal($key) { if (isset($this->ProfilerTotalCount[$key])) { return (int)$this->ProfilerTotalCount[$key]; } return 0; } function ProfilePoint($title, $level=1) { $trace_results = debug_backtrace(); $level = min($level,count($trace_results)-1); do { $point = $trace_results[$level]; $location = $point['file'].':'.$point['line']; $level++; $has_more = isset($trace_results[$level]); } while ($has_more && $point['function'] == $trace_results[$level]['function'] ); if (!isset($this->ProfilePoints[$title])) { $this->ProfilePoints[$title] = array(); } if (!isset($this->ProfilePoints[$title][$location])) { $this->ProfilePoints[$title][$location] = 0; } $this->ProfilePoints[$title][$location]++; } /** * Generates report * */ function printReport($returnResult = false, $clean_output_buffer = true) { if ($this->reportDone) { // don't print same report twice (in case if shutdown function used + compression + fatal error) return ''; } $this->profileFinish('script_runtime'); $this->breakOutofBuffering(!$returnResult); $debugger_start = memory_get_usage(); if (defined('SPACER_URL')) { $this->dummyImage = SPACER_URL; } $this->InitReport(); // set parameters required by AJAX // defined here, because user can define this contant while script is running, not event before debugger is started $this->safeDefine('DBG_RAISE_ON_WARNINGS', 0); $this->safeDefine('DBG_TOOLBAR_BUTTONS', 1); $this->appendSession(); // show php session if any // ensure, that 1st line of debug output always is this one: $top_line = '
[Reload Frame] [Hide Debugger] [Clear Debugger][Current Time: '.date('H:i:s').'] [File Size: #DBG_FILESIZE#]
'; $this->appendHTML($top_line); $this->moveToBegin(1); if (count($this->ProfilePoints)>0) { foreach($this->ProfilePoints as $point => $locations) { arsort($this->ProfilePoints[$point]); } $this->appendHTML($this->highlightString($this->print_r($this->ProfilePoints, true))); /*foreach ($this->ProfilePoints as $point => $locations) { foreach ($locations as $location => $occurences) { } }*/ } if ($this->constOn('DBG_SQL_PROFILE') && isset($this->ProfilerTotals['sql'])) { // sql query profiling was enabled -> show totals $this->appendHTML('SQL Total time: '.$this->ProfilerTotals['sql'].' Number of queries: '.$this->ProfilerTotalCount['sql']); } if ($this->constOn('DBG_PROFILE_INCLUDES') && isset($this->ProfilerTotals['includes'])) { // included file profiling was enabled -> show totals $this->appendHTML('Included Files Total time: '.$this->ProfilerTotals['includes'].' Number of includes: '.$this->ProfilerTotalCount['includes']); } if ($this->constOn('DBG_PROFILE_MEMORY')) { // detailed memory usage reporting by objects was enabled -> show totals $this->appendHTML('Memory used by Objects: '.round($this->ProfilerTotals['objects'] / 1024, 2).'Kb'); } if ($this->constOn('DBG_INCLUDED_FILES')) { $files = get_included_files(); $this->appendHTML('Included files:'); foreach ($files as $file) { $this->appendHTML($this->getFileLink($this->getLocalFile($file)).' ('.round(filesize($file) / 1024, 2).'Kb)'); } } if ($this->constOn('DBG_PROFILE_INCLUDES')) { $this->appendHTML('Included files statistics:'.( $this->constOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)':'')); $totals = Array( 'mem' => 0, 'time' => 0); $totals_configs = Array( 'mem' => 0, 'time' => 0); if (is_array($this->IncludesData['mem'])) { if ( $this->constOn('DBG_SORT_INCLUDES_MEM') ) { array_multisort($this->IncludesData['mem'], SORT_DESC, $this->IncludesData['file'], $this->IncludesData['time'], $this->IncludesData['level']); } foreach ($this->IncludesData['file'] as $key => $file_name) { $this->appendHTML( str_repeat(' -> ', ($this->IncludesData['level'][$key] >= 0 ? $this->IncludesData['level'][$key] : 0)).$file_name.' Mem: '.sprintf("%.4f Kb", $this->IncludesData['mem'][$key]/1024).' Time: '.sprintf("%.4f", $this->IncludesData['time'][$key])); if ($this->IncludesData['level'][$key] == 0) { $totals['mem'] += $this->IncludesData['mem'][$key]; $totals['time'] += $this->IncludesData['time'][$key]; } else if ($this->IncludesData['level'][$key] == -1) { $totals_configs['mem'] += $this->IncludesData['mem'][$key]; $totals_configs['time'] += $this->IncludesData['time'][$key]; } } $this->appendHTML('Sub-Total classes: '.' Mem: '.sprintf("%.4f Kb", $totals['mem']/1024).' Time: '.sprintf("%.4f", $totals['time'])); $this->appendHTML('Sub-Total configs: '.' Mem: '.sprintf("%.4f Kb", $totals_configs['mem']/1024).' Time: '.sprintf("%.4f", $totals_configs['time'])); $this->appendHTML('Grand Total: '.' Mem: '.sprintf("%.4f Kb", ($totals['mem']+$totals_configs['mem'])/1024).' Time: '.sprintf("%.4f", $totals['time']+$totals_configs['time'])); } } $skip_reporting = $this->constOn('DBG_SKIP_REPORTING') || $this->constOn('DBG_ZEND_PRESENT'); if (($this->_isAjax && !$this->constOn('DBG_SKIP_AJAX')) || !$skip_reporting) { $debug_file = $this->tempFolder.'/debug_'.$this->rowSeparator.'.txt'; if (file_exists($debug_file)) unlink($debug_file); $i = 0; $fp = fopen($debug_file, 'a'); $lineCount = count($this->Data); while ($i < $lineCount) { fwrite($fp, $this->prepareHTML($i).$this->rowSeparator); $i++; } fclose($fp); } if ($skip_reporting) { // let debugger write report and then don't output anything $this->reportDone = true; return ''; } $application =& kApplication::Instance(); $dbg_path = str_replace(FULL_PATH, '', $this->tempFolder); ob_start(); // the ProfilerTotals['error_handling'])) { $memory_used = $debugger_start; $this->ProfilerTotalCount['error_handling'] = 0; } else { $memory_used = $debugger_start - $this->ProfilerTotals['error_handling']; } if ($returnResult) { $ret = ob_get_contents(); if ($clean_output_buffer) { ob_end_clean(); } $ret .= $this->getShortReport($memory_used); $this->reportDone = true; return $ret; } else { if (!$this->constOn('DBG_HIDE_FULL_REPORT')) { $this->breakOutofBuffering(); } elseif ($clean_output_buffer) { ob_clean(); } echo $this->getShortReport($memory_used); $this->reportDone = true; } } /** * Format's memory usage report by debugger * * @return string * @access private */ function getShortReport($memory_used) { if ($this->constOn('DBG_TOOLBAR_BUTTONS')) { // we have sql & error count in toolbar, don't duplicate here $info = Array( 'Script Runtime' => 'PROFILE:script_runtime', 'SQL\'s Runtime' => 'PROFILE_T:sql', ); } else { // toolbar not visible, then show sql & error count too $info = Array ( 'Script Runtime' => 'PROFILE:script_runtime', 'SQL\'s Runtime' => 'PROFILE_T:sql', '-' => 'SEP:-', 'Notice / Warning' => 'PROFILE_TC:error_handling', 'SQLs Count' => 'PROFILE_TC:sql', ); } $ret = 'Application:'.$this->formatSize($memory_used).' ('.$memory_used.')'; foreach ($info as $title => $value_key) { list ($record_type, $record_data) = explode(':', $value_key, 2); switch ($record_type) { case 'PROFILE': // profiler totals value $Data =& $this->ProfilerData[$record_data]; $profile_time = ($Data['ends'] - $Data['begins']); // in seconds $ret .= ''.$title.':'.sprintf('%.5f', $profile_time).' s'; break; case 'PROFILE_TC': // profile totals record count $record_cell = ''; if ($record_data == 'error_handling' && $this->ProfilerTotalCount[$record_data] > 0) { $record_cell = ''; } $ret .= ''.$record_cell.$title.':'.$record_cell.''.$this->ProfilerTotalCount[$record_data].''; break; case 'PROFILE_T': // profile total $record_cell = ''; $total = array_key_exists($record_data, $this->ProfilerTotals) ? $this->ProfilerTotals[$record_data] : 0; $ret .= ''.$record_cell.$title.':'.$record_cell.''.sprintf('%.5f', $total).' s'; break; case 'SEP': $ret .= ''; break; } } return '
'.$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 = '') { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $errorType = $this->getErrorNameByCode($errno); if (!$errorType) { trigger_error('Unknown error type ['.$errno.']', E_USER_ERROR); return false; } if ($this->constOn('DBG_IGNORE_STRICT_ERRORS') && defined('E_STRICT') && ($errno == E_STRICT)) return; if (preg_match('/(.*)#([\d]+)$/', $errstr, $rets) ) { // replace short message with long one (due triger_error limitations on message size) $long_id = $rets[2]; $errstr = $this->longErrors[$long_id]; unset($this->longErrors[$long_id]); } if (strpos($errfile,'eval()\'d code') !== false) { $errstr = '[EVAL, line '.$errline.']: '.$errstr; $tmpStr = $errfile; $pos = strpos($tmpStr,'('); $errfile = substr($tmpStr, 0, $pos); $pos++; $errline = substr($tmpStr,$pos,strpos($tmpStr,')',$pos)-$pos); } $this->Data[] = Array('no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error'); $this->ProfilerData['error_handling']['ends'] = memory_get_usage(); $this->profilerAddTotal('error_handling', 'error_handling'); if (substr($errorType, 0, 5) == 'Fatal') { $this->IsFatalError = true; // append debugger report to data in buffer & clean buffer afterwards die( $this->breakOutofBuffering(false) . $this->printReport(true) ); } } function breakOutofBuffering($flush = true) { $buffer_content = Array(); while (ob_get_level()) { $buffer_content[] = ob_get_clean(); } $ret = implode('', array_reverse($buffer_content)); if ($flush) { echo $ret; flush(); } return $ret; } 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; } function printConstants($constants) { if (!is_array($constants)) { $constants = explode(',', $constants); } $contant_tpl = '%s%s'; $ret = ''; foreach ($constants as $constant_name) { $ret .= sprintf($contant_tpl, $constant_name, constant($constant_name)); } $ret .= '
'; $this->appendHTML($ret); } function AttachToApplication() { if (!$this->constOn('DBG_HANDLE_ERRORS')) { return true; } if (class_exists('kApplication')) { restore_error_handler(); // replace application error handler with own $this->Application =& kApplication::Instance(); $this->Application->Debugger =& $this; $this->Application->errorHandlers[] = Array (&$this, 'saveError'); } else { set_error_handler(Array(&$this, 'saveError')); } } /** * Returns HTML for tools section * * @return string */ function _getToolsHTML() { $html = '
System Tools:
'; return $html; } /** * Returns HTML for dom viewer section * * @return string */ function _getDomViewerHTML() { $html = '
DomViewer:
'; return $html; } } if (!function_exists('memory_get_usage')) { function memory_get_usage(){ return -1; } } if (!Debugger::constOn('DBG_ZEND_PRESENT')) { $debugger = new Debugger(); } if (Debugger::constOn('DBG_USE_SHUTDOWN_FUNC')) { register_shutdown_function( Array(&$debugger, 'printReport') ); } }