Index: branches/5.2.x/core/kernel/utility/debugger.php =================================================================== --- branches/5.2.x/core/kernel/utility/debugger.php (revision 16534) +++ branches/5.2.x/core/kernel/utility/debugger.php (revision 16535) @@ -1,2143 +1,2143 @@ = 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; } /** * 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 */ public static function ipMatch($ip_list, $separator = ';') { if ( php_sapi_name() == 'cli' ) { return false; } $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); $client_ip = self::getClientIp(); foreach ($ip_addresses as $ip_address) { if ( self::netMatch($ip_address, $client_ip) ) { $ip_match = true; break; } } return $ip_match; } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public static function getClientIp() { if ( self::$trustProxy ) { if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) { return $_SERVER['HTTP_CLIENT_IP']; } if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) { $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($client_ip as $ip_address) { $clean_ip_address = trim($ip_address); if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) { return $clean_ip_address; } } return ''; } } return $_SERVER['REMOTE_ADDR']; } /** * Checks, that given ip belongs to given subnet * * @param string $network * @param string $ip * @return bool * @access public */ public static function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) { $network = gethostbyname($network); } 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) { // single 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; } } /** * Main debugger class, that can be used with any In-Portal (or not) project */ class Debugger { const ROW_TYPE_ERROR = 'error'; const ROW_TYPE_WARNING = 'warning'; const ROW_TYPE_NOTICE = 'notice'; const ROW_TYPE_SQL = 'sql'; const ROW_TYPE_OTHER = 'other'; /** * Holds reference to global KernelApplication instance * * @var kApplication * @access private */ private $Application = null; /** * Stores last fatal error hash or 0, when no fatal error happened * * @var integer */ private $_fatalErrorHash = 0; private $_filterTypes = Array ('error', 'sql', 'other'); /** * Counts warnings on the page * * @var int * @access public */ public $WarningCount = 0; /** * Allows to track compile errors, like "stack-overflow" * * @var bool * @access private */ private $_compileError = false; /** * Debugger data for building report * * @var Array * @access private */ private $Data = Array (); /** * Holds information about each profiler record (start/end/description) * * @var Array * @access private */ private $ProfilerData = Array (); /** * Holds information about total execution time per profiler key (e.g. total sql time) * * @var Array * @access private */ private $ProfilerTotals = Array (); /** * Counts how much each of total types were called (e.g. total error count) * * @var Array * @access private */ private $ProfilerTotalCount = Array (); /** * Holds information about all profile points registered * * @var Array * @access private */ private $ProfilePoints = Array (); /** * Prevent recursion when processing debug_backtrace() function results * * @var Array * @access private */ private $RecursionStack = Array (); /** * Cross browser debugger report scrollbar width detection * * @var int * @access private */ private $scrollbarWidth = 0; /** * Remembers how much memory & time was spent on including files * * @var Array * @access public * @see kUtil::includeOnce */ public $IncludesData = Array (); /** * Remembers maximal include deep level * * @var int * @access public * @see kUtil::includeOnce */ public $IncludeLevel = 0; /** * Prevents report generation more then once * * @var bool * @access private */ private $_inReportPrinting = false; /** * Transparent spacer image used in case of none spacer image defined via SPACER_URL constant. * Used while drawing progress bars (memory usage, time usage, etc.) * * @var string * @access private */ private $dummyImage = ''; /** * Temporary files created by debugger will be stored here * * @var string * @access private */ private $tempFolder = ''; /** * Debug rows will be separated using this string before writing to debug file * * @var string * @access private */ private $rowSeparator = '@@'; /** * Base URL for debugger includes * * @var string * @access private */ private $baseURL = ''; /** * Sub-folder, where In-Portal is installed * * @var string * @access private */ private $basePath = ''; /** * Holds last recorded timestamp (for appendTimestamp) * * @var int * @access private */ private $LastMoment; /** * Determines, that current request is AJAX request * * @var bool * @access private */ private $_isAjax = false; /** * Data, parsed from the editor url. * * @var array */ protected $editorUrlData = array('url' => '', 'params' => array()); /** * Creates instance of debugger */ public function __construct() { global $start, $dbg_options; // check if user haven't defined DEBUG_MODE contant directly if ( defined('DEBUG_MODE') && DEBUG_MODE ) { die('error: constant DEBUG_MODE defined directly, please use $dbg_options array instead'); } if ( class_exists('kUtil') ) { DebuggerUtil::$trustProxy = kUtil::getSystemConfig()->get('TrustProxy'); } // check IP before enabling debug mode $ip_match = DebuggerUtil::ipMatch(isset($dbg_options['DBG_IP']) ? $dbg_options['DBG_IP'] : ''); if ( !$ip_match || (isset($_COOKIE['debug_off']) && $_COOKIE['debug_off']) ) { 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 & ~E_STRICT); // show errors on screen in case if not in Zend Studio debugging ini_set('display_errors', DebuggerUtil::constOn('DBG_ZEND_PRESENT') ? 0 : 1); // vertical scrollbar width differs in Firefox and other browsers $this->scrollbarWidth = $this->isGecko() ? 22 : 25; $this->appendRequest(); } /** * Set's default values to constants debugger uses * */ function InitDebugger() { global $dbg_options; unset($dbg_options['DBG_IP']); // Detect fact, that this session being debugged by Zend Studio foreach ($_COOKIE as $cookie_name => $cookie_value) { if (substr($cookie_name, 0, 6) == 'debug_') { DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 1); break; } } DebuggerUtil::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_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 'DBG_EDITOR_URL' => 'file://%F:%L', 'DBG_SHORTCUT' => 'F12', // Defines debugger activation shortcut (any symbols or Ctrl/Alt/Shift are allowed, e.g. Ctrl+Alt+F12) ); // 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 = array_merge($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; } // allows to validate unit configs via request variable if ( !array_key_exists('DBG_VALIDATE_CONFIGS', $dbg_constMap) ) { $dbg_constMap['DBG_VALIDATE_CONFIGS'] = array_key_exists('validate_configs', $_GET) ? (int)$_GET['validate_configs'] : 0; } // when validation configs, don't show sqls for better validation error displaying if ($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) { DebuggerUtil::safeDefine($dbg_constName, $dbg_constValue); } $this->parseEditorUrl(); } /** * Parses editor url. * * @return void */ protected function parseEditorUrl() { $components = parse_url(DBG_EDITOR_URL); $this->editorUrlData['url'] = $components['scheme'] . '://' . $components['host']; if ( isset($components['path']) ) { $this->editorUrlData['url'] .= $components['path']; } if ( isset($components['query']) ) { parse_str(html_entity_decode($components['query']), $this->editorUrlData['params']); } } /** * Performs debugger initialization * * @return void */ private function InitReport() { if ( !class_exists('kApplication') ) { return; } $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->InitDone ? $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 . (defined('PORT') ? ':' . PORT : '') . rtrim(BASE_PATH, '/') . $kernel_path . '/utility/debugger'; // store debugger cookies at this path $this->basePath = rtrim(BASE_PATH, '/'); // save debug output in this folder $this->tempFolder = defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache'; } /** * Appends all passed variable values (without variable names) to debug output * * @return void * @access public */ public 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); } } /** * Transforms collected data at given index into human-readable HTML to place in debugger report * * @param int $dataIndex * @return string * @access private */ private function prepareHTML($dataIndex) { static $errors_displayed = 0; $Data =& $this->Data[$dataIndex]; if ( $Data['debug_type'] == 'html' ) { return $Data['html']; } switch ($Data['debug_type']) { case 'error': $errors_displayed++; $fileLink = $this->getFileLink($Data['file'], $Data['line']); $ret = '' . $this->getErrorNameByCode($Data['no']) . ' (#' . $errors_displayed . '): ' . $Data['str']; $ret .= ' in ' . $fileLink . ' on line ' . $Data['line'] . ''; return $ret; break; case 'exception': $fileLink = $this->getFileLink($Data['file'], $Data['line']); $ret = '' . $Data['exception_class'] . ': ' . $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); $subtitle = array_key_exists('subtitle', $Data) ? ' (' . $Data['subtitle'] . ')' : ''; $ret = 'Name' . $subtitle . ': ' . $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; } } /** * Returns row type for debugger row. * * @param integer $data_index Index of the row. * * @return string */ protected function getRowType($data_index) { $data = $this->Data[$data_index]; switch ($data['debug_type']) { case 'html': if ( strpos($data['html'], 'SQL Total time') !== false ) { return self::ROW_TYPE_SQL; } break; case 'error': $error_map = array( 'Fatal Error' => self::ROW_TYPE_ERROR, 'Warning' => self::ROW_TYPE_WARNING, 'Notice' => self::ROW_TYPE_NOTICE, + 'Deprecation Notice' => self::ROW_TYPE_NOTICE, ); return $error_map[$this->getErrorNameByCode($data['no'])]; break; case 'exception': return self::ROW_TYPE_ERROR; break; case 'profiler': if ( preg_match('/^sql_/', $data['profile_key']) ) { return self::ROW_TYPE_SQL; } break; } return self::ROW_TYPE_OTHER; } /** * Returns debugger report window width excluding scrollbar * * @return int * @access private */ private 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 * @access private */ private 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 mixed $p_array Value to be printed. * @param boolean $return_output Return output or print it out. * @param integer $tab_count Offset in tabs. * * @return string */ private function print_r(&$p_array, $return_output = false, $tab_count = -1) { static $first_line = true; // not an array at all if ( !is_array($p_array) ) { switch ( gettype($p_array) ) { case 'NULL': return 'NULL' . "\n"; break; case 'object': return $this->processObject($p_array, $tab_count); break; case 'resource': return (string)$p_array . "\n"; break; default: // number or string if ( strlen($p_array) > 200 ) { $p_array = substr($p_array, 0, 50) . ' ...'; } return $p_array . "\n"; break; } } $output = ''; if ( count($p_array) > 50 ) { $array = array_slice($p_array, 0, 50); $array[] = '...'; } else { $array = $p_array; } $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; } /** * Returns string representation of given object (more like print_r, but with recursion prevention check) * * @param Object $object * @param int $tab_count * @return string * @access private */ private 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 * @access public */ public 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 example "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; } /** * Highlights given string using "highlight_string" method * * @param string $string * @return string * @access public */ public 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 * @access private */ private 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 $line_number line number in file where error is found * @param string $title text to show on file edit link * @return string * @access public */ public function getFileLink($file, $line_number = 1, $title = '') { if ( !$title ) { $title = str_replace('/', '\\', $this->getLocalFile($file)); } $local_file = $this->getLocalFile($file); $url_params = $this->editorUrlData['params']; foreach ( $url_params as $param_name => $param_value ) { $url_params[$param_name] = str_replace( array('%F', '%L'), array($local_file, $line_number), $param_value ); } $url = $this->editorUrlData['url'] . '?' . http_build_query($url_params); return '' . $title . ''; } /** * Converts filepath on server to filepath in mapped DocumentRoot on developer pc * * @param string $remoteFile * @return string * @access private */ private function getLocalFile($remoteFile) { return preg_replace('/^' . preg_quote(DOC_ROOT, '/') . '/', DBG_LOCAL_BASE_PATH, $remoteFile, 1); } /** * Appends call trace till this method call * * @param int $levels_to_shift * @return void * @access public */ public function appendTrace($levels_to_shift = 1) { $levels_shifted = 0; $trace = debug_backtrace(); while ( $levels_shifted < $levels_to_shift ) { array_shift($trace); $levels_shifted++; } $this->Data[] = Array ('trace' => $trace, 'debug_type' => 'trace'); } /** * Appends call trace till this method call * * @param Exception $exception * @return void * @access private */ private function appendExceptionTrace(&$exception) { $trace = $exception->getTrace(); $this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace'); } /** * Adds memory usage statistics * * @param string $msg * @param int $used * @return void * @access public */ public function appendMemoryUsage($msg, $used = null) { if ( !isset($used) ) { $used = round(memory_get_usage() / 1024); } $this->appendHTML('Memory usage ' . $msg . ' ' . $used . 'Kb'); } /** * Appends HTML code without transformations * * @param string $html * @return void * @access public */ public function appendHTML($html) { $this->Data[] = Array ('html' => $html, 'debug_type' => 'html'); } /** * Returns instance of FirePHP class * * @return FirePHP * @link http://www.firephp.org/HQ/Use.htm */ function firePHP() { require_once('FirePHPCore/FirePHP.class.php'); return FirePHP::getInstance(true); } /** * 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 * @access public */ public 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 * @return void * @access private */ private function moveToBegin($debugLineCount) { $lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount); $this->Data = array_merge($lines, $this->Data); } /** * Moves all debugger report lines after $debugLineCount into $new_row position * * @param int $new_row * @param int $debugLineCount * @return void * @access private */ private 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); } /** * Appends HTTP REQUEST information to debugger report * * @return void * @access private */ private 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(); ?> $_GET, 'PO' => $_POST, 'CO' => $_COOKIE); foreach ($super_globals as $prefix => $data) { foreach ($data as $key => $value) { if ( !is_array($value) && trim($value) == '' ) { $value = 'no value'; } else { $value = htmlspecialchars($this->print_r($value, true), ENT_QUOTES, 'UTF-8'); } echo ''; } } ?>
SrcNameValue
' . $prefix . '' . $key . '' . $value . '
appendHTML(ob_get_contents()); ob_end_clean(); } /** * Appends php session content to debugger output * * @return void * @access private */ private function appendSession() { if ( isset($_SESSION) && $_SESSION ) { $this->appendHTML('PHP Session: [' . ini_get('session.name') . ']'); $this->dumpVars($_SESSION); $this->moveToBegin(2); } } /** * Starts profiling of a given $key * * @param string $key * @param string $description * @param int $timeStamp * @return void * @access public */ public function profileStart($key, $description = null, $timeStamp = null) { if ( !isset($timeStamp) ) { $timeStamp = microtime(true); } $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 ) { if ( !isset($trace_results[$i]['file']) ) { $i++; continue; } $trace_file = basename($trace_results[$i]['file']); if ( $trace_file != 'db_connection.php' && $trace_file != 'db_load_balancer.php' && $trace_file != 'adodb.inc.php' ) { break; } $i++; } $this->ProfilerData[$key]['file'] = $trace_results[$i]['file']; $this->ProfilerData[$key]['line'] = $trace_results[$i]['line']; if ( isset($trace_results[$i + 1]['object']) && isset($trace_results[$i + 1]['object']->Prefix) ) { /** @var kBase $object */ $object =& $trace_results[$i + 1]['object']; $prefix_special = rtrim($object->Prefix . '.' . $object->Special, '.'); $this->ProfilerData[$key]['prefix_special'] = $prefix_special; } unset($trace_results); } $this->Data[] = Array ('profile_key' => $key, 'debug_type' => 'profiler'); } /** * Ends profiling for a given $key * * @param string $key * @param string $description * @param int $timeStamp * @return void * @access public */ public function profileFinish($key, $description = null, $timeStamp = null) { if ( !isset($timeStamp) ) { $timeStamp = microtime(true); } $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]); if ( $func_arguments[6] ) { $this->profilerAddTotal('cachable_queries', $key); $this->ProfilerData[$key]['subtitle'] = 'cachable'; } if ( (string)$func_arguments[7] !== '' ) { $additional[] = Array ('name' => 'Server #', 'value' => $func_arguments[7]); } if ( array_key_exists('prefix_special', $this->ProfilerData[$key]) ) { $additional[] = Array ('name' => 'PrefixSpecial', 'value' => $this->ProfilerData[$key]['prefix_special']); } $this->ProfilerData[$key]['additional'] =& $additional; } } /** * Collects total execution time from profiler record * * @param string $total_key * @param string $key * @param int $value * @return void * @access public */ public 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]++; } /** * Traces relative code execution speed between this method calls * * @param string $message * @return void * @access public */ public function appendTimestamp($message) { global $start; $time = microtime(true); $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; } /** * Returns unique ID for each method call * * @return int * @access public */ public 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; } /** * Returns error name based on it's code * * @param int $error_code * @return string * @access private */ private function getErrorNameByCode($error_code) { $error_map = Array ( 'Fatal Error' => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE), 'Warning' => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING), 'Notice' => Array (E_NOTICE, E_USER_NOTICE, E_STRICT), ); if ( defined('E_DEPRECATED') ) { - // since PHP 5.3 - $error_map['Notice'][] = E_DEPRECATED; - $error_map['Notice'][] = E_USER_DEPRECATED; + // Since PHP 5.3. + $error_map['Deprecation Notice'] = 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 missing key too) * * @param string $key * @return int * @access private */ private function getProfilerTotal($key) { if ( isset($this->ProfilerTotalCount[$key]) ) { return (int)$this->ProfilerTotalCount[$key]; } return 0; } /** * Counts how much calls were made to a place, where this method is called (basic version of profiler) * * @param string $title * @param int $level * @return void * @access public */ public 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 * * @param boolean $return_result Returns or output report contents. * @param boolean $clean_output_buffer Clean output buffers before displaying anything. * @param boolean $is_shutdown_func Called from shutdown function. * * @return string */ public function printReport($return_result = false, $clean_output_buffer = true, $is_shutdown_func = false) { if ( $this->_inReportPrinting ) { // don't print same report twice (in case if shutdown function used + compression + fatal error) return ''; } $this->_inReportPrinting = true; $last_error = error_get_last(); if ( !is_null($last_error) && $is_shutdown_func ) { $this->saveError( $last_error['type'], $last_error['message'], $last_error['file'], $last_error['line'], null, $is_shutdown_func ); } $this->profileFinish('script_runtime'); $this->_breakOutOfBuffering(!$return_result); $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 constant while script is running, not event before debugger is started DebuggerUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 0); DebuggerUtil::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->getFilterDropdown() . '
'; $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))); } if ( DebuggerUtil::constOn('DBG_SQL_PROFILE') && isset($this->ProfilerTotals['sql']) ) { // sql query profiling was enabled -> show totals if ( array_key_exists('cachable_queries', $this->ProfilerTotalCount) ) { $append = ' Cachable queries: ' . $this->ProfilerTotalCount['cachable_queries']; } else { $append = ''; } $this->appendHTML('SQL Total time: ' . $this->ProfilerTotals['sql'] . ' Number of queries: ' . $this->ProfilerTotalCount['sql'] . $append); } if ( DebuggerUtil::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 ( DebuggerUtil::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 ( DebuggerUtil::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 ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') ) { $totals = $totals_configs = Array ('mem' => 0, 'time' => 0); $this->appendHTML('Included files statistics:' . (DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)' : '')); if ( is_array($this->IncludesData['mem']) ) { if ( DebuggerUtil::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]; } elseif ( $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 = DebuggerUtil::constOn('DBG_SKIP_REPORTING') || DebuggerUtil::constOn('DBG_ZEND_PRESENT'); if ( ($this->_isAjax && !DebuggerUtil::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 ) { $html = $this->prepareHTML($i); $row_type = $this->getRowType($i); fwrite($fp, json_encode(Array ('html' => $html, 'row_type' => $row_type)) . $this->rowSeparator); $i++; } fclose($fp); } if ( $skip_reporting ) { // let debugger write report and then don't output anything return ''; } $application =& kApplication::Instance(); $dbg_path = str_replace(FULL_PATH, '', $this->tempFolder); $debugger_params = Array ( 'FilterTypes' => $this->_filterTypes, 'RowSeparator' => $this->rowSeparator, 'ErrorsCount' => (int)$this->getProfilerTotal('error_handling'), 'IsFatalError' => $this->_fatalErrorHappened(), 'SQLCount' => (int)$this->getProfilerTotal('sql'), 'SQLTime' => isset($this->ProfilerTotals['sql']) ? sprintf('%.5f', $this->ProfilerTotals['sql']) : 0, 'ScriptTime' => sprintf('%.5f', $this->ProfilerData['script_runtime']['ends'] - $this->ProfilerData['script_runtime']['begins']), 'ScriptMemory' => DebuggerUtil::formatSize($this->getMemoryUsed($debugger_start)), 'Shortcut' => DBG_SHORTCUT, ); ob_start(); // the getShortReport($this->getMemoryUsed($debugger_start)); return $ret; } else { if ( !DebuggerUtil::constOn('DBG_HIDE_FULL_REPORT') ) { $this->_breakOutOfBuffering(); } elseif ( $clean_output_buffer ) { ob_clean(); } echo $this->getShortReport($this->getMemoryUsed($debugger_start)); } return ''; } function getFilterDropdown() { $filter_options = ''; foreach ( $this->_filterTypes as $filter_type ) { $filter_options .= ''; } return 'Show: '; } function getMemoryUsed($debugger_start) { if ( !isset($this->ProfilerTotals['error_handling']) ) { $memory_used = $debugger_start; $this->ProfilerTotalCount['error_handling'] = 0; } else { $memory_used = $debugger_start - $this->ProfilerTotals['error_handling']; } return $memory_used; } /** * Format's memory usage report by debugger * * @param int $memory_used * @return string * @access private */ private function getShortReport($memory_used) { if ( DebuggerUtil::constOn('DBG_TOOLBAR_BUTTONS') ) { // evenrything is in toolbar - don't duplicate return ''; } 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:' . DebuggerUtil::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 . '
'; } /** * Detects if there was a fatal error at some point * * @return boolean */ private function _fatalErrorHappened() { return $this->_fatalErrorHash !== 0; } /** * Creates error hash * * @param string $errfile File, where error happened. * @param integer $errline Line in file, where error happened. * * @return integer */ private function _getErrorHash($errfile, $errline) { return crc32($errfile . ':' . $errline); } /** * User-defined error handler * * @param integer $errno Error code. * @param string $errstr Error message. * @param string $errfile Error file. * @param integer $errline Error line. * @param array $errcontext Error context. * @param boolean $is_shutdown_func Called from shutdown function. * * @return boolean * @throws Exception When unknown error code given. */ public function saveError( $errno, $errstr, $errfile = null, $errline = null, array $errcontext = null, $is_shutdown_func = false ) { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $errorType = $this->getErrorNameByCode($errno); if (!$errorType) { throw new Exception('Unknown error type [' . $errno . ']'); return false; } elseif ( substr($errorType, 0, 5) == 'Fatal' ) { $this->_fatalErrorHash = $this->_getErrorHash($errfile, $errline); $this->appendTrace(4); } $this->expandError($errstr, $errfile, $errline); $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 ($errorType == 'Warning') { $this->WarningCount++; } if ( $this->_fatalErrorHappened() && $this->_getErrorHash($errfile, $errline) === $this->_fatalErrorHash ) { // Append debugger report to data in buffer & clean buffer afterwards. echo $this->_breakOutOfBuffering(false) . $this->printReport(true); if ( !$is_shutdown_func ) { exit; } } return true; } /** * Adds exception details into debugger but don't cause fatal error * * @param Exception $exception * @return void * @access public */ public function appendException($exception) { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $this->appendExceptionTrace($exception); $errno = $exception->getCode(); $errstr = $exception->getMessage(); $errfile = $exception->getFile(); $errline = $exception->getLine(); $this->expandError($errstr, $errfile, $errline); $this->Data[] = Array ( 'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'exception_class' => get_class($exception), 'debug_type' => 'exception' ); $this->ProfilerData['error_handling']['ends'] = memory_get_usage(); $this->profilerAddTotal('error_handling', 'error_handling'); } /** * User-defined exception handler * * @param Exception $exception * @return void * @access public */ public function saveException($exception) { $this->appendException($exception); $this->_fatalErrorHash = $this->_getErrorHash($exception->getFile(), $exception->getLine()); // Append debugger report to data in buffer & clean buffer afterwards. echo $this->_breakOutOfBuffering(false) . $this->printReport(true); } /** * Transforms short error messages into long ones * * @param string $errstr * @param string $errfile * @param int $errline * @return void * @access private */ private function expandError(&$errstr, &$errfile, &$errline) { $errstr = kLogger::expandMessage($errstr); list ($errno, $errstr, $sql) = kLogger::parseDatabaseError($errstr); if ( $errno != 0 ) { $errstr = '' . $errstr . ' (' . $errno . ')
SQL: ' . $this->formatSQL($sql); } 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); } } /** * Break buffering in case if fatal error is happened in the middle * * @param bool $flush * @return string * @access private */ private 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; } /** * Saves given message to "vb_debug.txt" file in DocumentRoot * * @param string $msg * @return void * @access public */ public function saveToFile($msg) { $fp = fopen($_SERVER['DOCUMENT_ROOT'] . '/vb_debug.txt', 'a'); fwrite($fp, $msg . "\n"); fclose($fp); } /** * Prints given constant values in a table * * @param mixed $constants * @return void * @access public */ public function printConstants($constants) { if ( !is_array($constants) ) { $constants = explode(',', $constants); } $constant_tpl = '%s%s'; $ret = ''; foreach ($constants as $constant_name) { $ret .= sprintf($constant_tpl, $constant_name, constant($constant_name)); } $ret .= '
'; $this->appendHTML($ret); } /** * Attaches debugger to Application * * @return void * @access public */ public function AttachToApplication() { if ( !DebuggerUtil::constOn('DBG_HANDLE_ERRORS') ) { return; } if ( class_exists('kApplication') ) { $this->Application =& kApplication::Instance(); $this->Application->Debugger = $this; } // kLogger will auto-detect these automatically // error/exception handlers registered before debugger will be removed! set_error_handler( Array ($this, 'saveError') ); set_exception_handler( Array ($this, 'saveException') ); } /** * Returns HTML for tools section * * @return string * @access private */ private function _getToolsHTML() { $html = '
System Tools:
'; return $html; } /** * Returns HTML for dom viewer section * * @return string * @access private */ private function _getDomViewerHTML() { $html = '
DomViewer:
'; return $html; } } if ( !function_exists('memory_get_usage') ) { // PHP 4.x and compiled without --enable-memory-limit option function memory_get_usage() { return -1; } } if ( !DebuggerUtil::constOn('DBG_ZEND_PRESENT') ) { $debugger = new Debugger(); } if ( DebuggerUtil::constOn('DBG_USE_SHUTDOWN_FUNC') ) { register_shutdown_function(array(&$debugger, 'printReport'), false, true, true); } } Index: branches/5.2.x/core/kernel/globals.php =================================================================== --- branches/5.2.x/core/kernel/globals.php (revision 16534) +++ branches/5.2.x/core/kernel/globals.php (revision 16535) @@ -1,1076 +1,1145 @@ $array2_value ) { if ( isset($array1[$array2_key]) ) { $array1[$array2_key] = self::array_merge_recursive($array1[$array2_key], $array2_value); } else { $array1[$array2_key] = $array2_value; } } return $array1; } /** * Prepend a reference to an element to the beginning of an array. * Renumbers numeric keys, so $value is always inserted to $array[0] * * @param $array array * @param $value mixed * @return int * @access public */ public static function array_unshift_ref(&$array, &$value) { $return = array_unshift($array,''); $array[0] =& $value; return $return; } /** * Rename key in associative array, maintaining keys order * * @param Array $array Associative Array * @param mixed $old Old key name * @param mixed $new New key name * @access public */ public static function array_rename_key(&$array, $old, $new) { $new_array = Array (); foreach ($array as $key => $val) { $new_array[ $key == $old ? $new : $key] = $val; } $array = $new_array; } /** * Same as print_r, but outputs result on screen or in debugger report (when in debug mode) * * @param Array $data * @param string $label * @param bool $on_screen * @access public */ public static function print_r($data, $label = '', $on_screen = false) { $is_debug = false; if ( class_exists('kApplication') && !$on_screen ) { $application =& kApplication::Instance(); $is_debug = $application->isDebugMode(); } if ( $is_debug && isset($application) ) { if ( $label ) { $application->Debugger->appendHTML('' . $label . ''); } $application->Debugger->dumpVars($data); } else { if ( $label ) { echo '' . $label . '
'; } echo '
', print_r($data, true), '
'; } } /** * Define constant if it was not already defined before * * @param string $const_name * @param string $const_value * @access public */ public static function safeDefine($const_name, $const_value) { if ( !defined($const_name) ) { define($const_name, $const_value); } } /** * Instantiate kSystemConfig class once and store locally * * @access public */ public static function getSystemConfig() { static $system_config; if ( !isset($system_config) ) { $system_config = new kSystemConfig(); } return $system_config; } /** * Same as "include_once", but also profiles file includes in debug mode and DBG_PROFILE_INCLUDES constant is set * * @param string $file * @access public */ public static function includeOnce($file) { global $debugger; if ( defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger) && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) { if ( in_array($file, get_included_files()) ) { return ; } global $debugger; /*$debugger->IncludeLevel++; $before_mem = memory_get_usage();*/ $debugger->ProfileStart('inc_'.crc32($file), $file); include_once($file); $debugger->ProfileFinish('inc_'.crc32($file)); $debugger->profilerAddTotal('includes', 'inc_'.crc32($file)); /*$used_mem = memory_get_usage() - $before_mem; $debugger->IncludeLevel--; $debugger->IncludesData['file'][] = str_replace(FULL_PATH, '', $file); $debugger->IncludesData['mem'][] = $used_mem; $debugger->IncludesData['time'][] = $used_time; $debugger->IncludesData['level'][] = $debugger->IncludeLevel;*/ } else { include_once($file); } } /** * Checks if given string is a serialized array * * @param string $string * @return bool * @access public */ public static function IsSerialized($string) { if ( is_array($string) ) { return false; } return preg_match('/a:([\d]+):{/', $string); } /** * Generates password of given length * * @param int $length * @return string * @access public */ public static function generatePassword($length = 10) { $pass_length = $length; $p1 = Array ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z'); $p2 = Array ('a','e','i','o','u'); $p3 = Array ('1','2','3','4','5','6','7','8','9'); $p4 = Array ('(','&',')',';','%'); // if you need real strong stuff // how much elements in the array // can be done with a array count but counting once here is faster $s1 = 21;// this is the count of $p1 $s2 = 5; // this is the count of $p2 $s3 = 9; // this is the count of $p3 $s4 = 5; // this is the count of $p4 // possible readable combinations $c1 = '121'; // will be like 'bab' $c2 = '212'; // will be like 'aba' $c3 = '12'; // will be like 'ab' $c4 = '3'; // will be just a number '1 to 9' if you dont like number delete the 3 //$c5 = '4'; // uncomment to active the strong stuff $comb = '4'; // the amount of combinations you made above (and did not comment out) for ($p = 0; $p < $pass_length;) { mt_srand((double)microtime() * 1000000); $strpart = mt_rand(1, $comb); // checking if the stringpart is not the same as the previous one if ($strpart != $previous) { $pass_structure .= ${'c' . $strpart}; // shortcutting the loop a bit $p = $p + mb_strlen(${'c' . $strpart}); } $previous = $strpart; } // generating the password from the structure defined in $pass_structure for ($g = 0; $g < mb_strlen($pass_structure); $g++) { mt_srand((double)microtime() * 1000000); $sel = mb_substr($pass_structure, $g, 1); $pass .= ${'p' . $sel}[ mt_rand(0,-1+${'s'.$sel}) ]; } return $pass; } /** * submits $url with $post as POST * * @param string $url * @param mixed $data * @param Array $headers * @param string $request_type * @param Array $curl_options * @return string * @access public * @deprecated */ public static function curl_post($url, $data, $headers = NULL, $request_type = 'POST', $curl_options = NULL) { $application =& kApplication::Instance(); /** @var kCurlHelper $curl_helper */ $curl_helper = $application->recallObject('CurlHelper'); if ($request_type == 'POST') { $curl_helper->SetRequestMethod(kCurlHelper::REQUEST_METHOD_POST); } $curl_helper->SetRequestData($data); if (!is_null($headers)) { // not an associative array, so don't use kCurlHelper::SetHeaders method $curl_helper->setOptions( Array (CURLOPT_HTTPHEADER => $headers) ); } if (is_array($curl_options)) { $curl_helper->setOptions($curl_options); } $curl_helper->followLocation = false; $ret = $curl_helper->Send($url); $GLOBALS['curl_errorno'] = $curl_helper->lastErrorCode; $GLOBALS['curl_error'] = $curl_helper->lastErrorMsg; return $ret; } /** * Checks if constant is defined and has positive value * * @param string $const_name * @return bool * @access public */ public static function constOn($const_name) { return defined($const_name) && constant($const_name); } /** * Converts KG to Pounds * * @param float $kg * @param bool $pounds_only * @return float * @access public */ public static function Kg2Pounds($kg, $pounds_only = false) { $major = floor( round($kg / self::POUND_TO_KG, 3) ); $minor = abs(round(($kg - $major * self::POUND_TO_KG) / self::POUND_TO_KG * 16, 2)); if ($pounds_only) { $major += round($minor * 0.0625, 2); $minor = 0; } return array($major, $minor); } /** * Converts Pounds to KG * * @param float $pounds * @param float $ounces * @return float * @access public */ public static function Pounds2Kg($pounds, $ounces = 0.00) { return round(($pounds + ($ounces / 16)) * self::POUND_TO_KG, 5); } /** * Formats file/memory size in nice way * * @param int $bytes * @return string * @access public */ public static 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; } /** * Enter description here... * * @param resource $filePointer the file resource to write to * @param Array $data the data to write out * @param string $delimiter the field separator * @param string $enclosure symbol to enclose field data to * @param string $recordSeparator symbols to separate records with * @access public */ public static function fputcsv($filePointer, $data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") { fwrite($filePointer, self::getcsvline($data, $delimiter, $enclosure, $recordSeparator)); } /** * Enter description here... * * @param Array $data the data to write out * @param string $delimiter the field separator * @param string $enclosure symbol to enclose field data to * @param string $recordSeparator symbols to separate records with * @return string * @access public */ public static function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") { ob_start(); $fp = fopen('php://output', 'w'); fputcsv($fp, $data, $delimiter, $enclosure); fclose($fp); $ret = ob_get_clean(); if ( $recordSeparator != "\n" ) { return substr($ret, 0, -1) . $recordSeparator; } return $ret; } /** * Allows to replace #section# within any string with current section * * @param string $string * @return string * @access public */ public static function replaceModuleSection($string) { $application =& kApplication::Instance(); $module_section = $application->RecallVar('section'); if ($module_section) { // substitute section instead of #section# parameter in title preset name $module_section = explode(':', $module_section); $section = preg_replace('/(configuration|configure)_(.*)/i', '\\2', $module_section[count($module_section) == 2 ? 1 : 0]); $string = str_replace('#section#', mb_strtolower($section), $string); } return $string; } /** * 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 * @access public */ public static function ipMatch($ip_list, $separator = ';') { if ( php_sapi_name() == 'cli' ) { return false; } $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); $application =& kApplication::Instance(); $client_ip = $application->getClientIp(); foreach ($ip_addresses as $ip_address) { if ( self::netMatch($ip_address, $client_ip) ) { $ip_match = true; break; } } return $ip_match; } /** * Checks, that given ip belongs to given subnet * * @param string $network * @param string $ip * @return bool * @access public */ public static function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) { $network = gethostbyname($network); } 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) { // single 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; } /** * Returns mime type corresponding to given file * @param string $file * @return string * @access public */ public static function mimeContentType($file) { $ret = self::vendorMimeContentType($file); if ( $ret ) { // vendor-specific mime types override any automatic detection return $ret; } if ( function_exists('finfo_open') && function_exists('finfo_file') ) { $mime_magic_resource = finfo_open(FILEINFO_MIME_TYPE); if ( $mime_magic_resource ) { $ret = finfo_file($mime_magic_resource, $file); finfo_close($mime_magic_resource); } } elseif ( function_exists('mime_content_type') ) { $ret = mime_content_type($file); } return $ret ? $ret : self::mimeContentTypeByExtension($file); } /** * Determines vendor-specific mime type from a given file * * @param string $file * @return bool * @access public * @static */ public static function vendorMimeContentType($file) { $file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION)); $mapping = Array ( 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' ); return isset($mapping[$file_extension]) ? $mapping[$file_extension] : false; } /** * Detects mime type of the file purely based on it's extension * * @param string $file * @return string * @access public */ public static function mimeContentTypeByExtension($file) { $file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION)); $mapping = '(xls:application/excel)(hqx:application/macbinhex40)(doc,dot,wrd:application/msword)(pdf:application/pdf) (pgp:application/pgp)(ps,eps,ai:application/postscript)(ppt:application/powerpoint)(rtf:application/rtf) (tgz,gtar:application/x-gtar)(gz:application/x-gzip)(php,php3:application/x-httpd-php)(js:application/x-javascript) (ppd,psd:application/x-photoshop)(swf,swc,rf:application/x-shockwave-flash)(tar:application/x-tar)(zip:application/zip) (mid,midi,kar:audio/midi)(mp2,mp3,mpga:audio/mpeg)(ra:audio/x-realaudio)(wav:audio/wav)(bmp:image/bitmap)(bmp:image/bitmap) (gif:image/gif)(iff:image/iff)(jb2:image/jb2)(jpg,jpe,jpeg:image/jpeg)(jpx:image/jpx)(png:image/png)(tif,tiff:image/tiff) (wbmp:image/vnd.wap.wbmp)(xbm:image/xbm)(css:text/css)(txt:text/plain)(htm,html:text/html)(xml:text/xml) (mpg,mpe,mpeg:video/mpeg)(qt,mov:video/quicktime)(avi:video/x-ms-video)(eml:message/rfc822) (sxw:application/vnd.sun.xml.writer)(sxc:application/vnd.sun.xml.calc)(sxi:application/vnd.sun.xml.impress) (sxd:application/vnd.sun.xml.draw)(sxm:application/vnd.sun.xml.math) (odt:application/vnd.oasis.opendocument.text)(oth:application/vnd.oasis.opendocument.text-web) (odm:application/vnd.oasis.opendocument.text-master)(odg:application/vnd.oasis.opendocument.graphics) (odp:application/vnd.oasis.opendocument.presentation)(ods:application/vnd.oasis.opendocument.spreadsheet) (odc:application/vnd.oasis.opendocument.chart)(odf:application/vnd.oasis.opendocument.formula) (odi:application/vnd.oasis.opendocument.image)'; if ( preg_match('/[\(,]' . $file_extension . '[,]{0,1}.*?:(.*?)\)/s', $mapping, $regs) ) { return $regs[1]; } return 'application/octet-stream'; } /** * Strips ".tmp" suffix (added by flash uploader) from a filename * * @param string $file * @return string * @access public * @static */ public static function removeTempExtension($file) { return preg_replace('/(_[\d]+)?\.tmp$/', '', $file); } /** * Return param value and removes it from params array * * @param string $name * @param Array $params * @param bool $default * @return string */ public static function popParam($name, &$params, $default = false) { if ( isset($params[$name]) ) { $value = $params[$name]; unset($params[$name]); return $value; } return $default; } /** * Generate subpath from hashed value * * @param string $name * @param int $levels * @return string */ public static function getHashPathForLevel($name, $levels = 2) { if ( $levels == 0 ) { return ''; } else { $path = ''; $hash = md5($name); for ($i = 0; $i < $levels; $i++) { $path .= substr($hash, $i, 1) . '/'; } return $path; } } /** * Calculates the crc32 polynomial of a string (always positive number) * * @param string $str * @return int */ public static function crc32($str) { return sprintf('%u', crc32($str)); } /** * Returns instance of DateTime class with date set based on timestamp * * @static * @param int $timestamp * @return DateTime * @access public */ public static function dateFromTimestamp($timestamp) { if ( version_compare(PHP_VERSION, '5.3.0', '<') ) { $date = new DateTime('@' . $timestamp); $date->setTimezone(new DateTimeZone(date_default_timezone_get())); } else { $date = new DateTime(); $date->setTimestamp($timestamp); } return $date; } /** * Returns timestamp from given DateTime class instance * * @static * @param DateTime $date_time * @return int|string * @access public */ public static function timestampFromDate(DateTime $date_time) { if ( version_compare(PHP_VERSION, '5.3.0', '<') ) { return $date_time->format('U'); } return $date_time->getTimestamp(); } /** * Generates random numeric id * * @static * @return string * @access public */ public static 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; } /** * Changes script resource limits. Omitted argument results in limit removal. * * @static * @param string|int $memory_limit * @param int $time_limit * @return void * @access public */ public static function setResourceLimit($memory_limit = null, $time_limit = null) { set_time_limit(isset($time_limit) ? $time_limit : 0); ini_set('memory_limit', isset($memory_limit) ? $memory_limit : -1); } /** * Escapes a string. * * @param string $text Text to escape. * @param string $strategy Escape strategy. * * @return string * @throws InvalidArgumentException When unknown escape strategy is given. */ public static function escape($text, $strategy = null) { if ( !isset($strategy) ) { $strategy = self::$escapeStrategy; } if ( strpos($strategy, '+') !== false ) { $previous_strategy = ''; $strategies = explode('+', $strategy); foreach ($strategies as $current_strategy) { // apply default strategy if ( $current_strategy == '' ) { $current_strategy = self::$escapeStrategy; } // don't double-escape if ( $current_strategy != $previous_strategy ) { $text = self::escape($text, $current_strategy); $previous_strategy = $current_strategy; } } return $text; } if ( $strategy == self::ESCAPE_HTML ) { return htmlspecialchars($text, ENT_QUOTES, CHARSET); } if ( $strategy == self::ESCAPE_JS ) { // TODO: consider using "addcslashes", because "addslashes" isn't really for JavaScript escaping (according to docs) $text = addslashes($text); $text = str_replace(array("\r", "\n"), array('\r', '\n'), $text); $text = str_replace('', "", $text); return $text; } if ( $strategy == self::ESCAPE_URL ) { return rawurlencode($text); } if ( $strategy == self::ESCAPE_RAW ) { return $text; } throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); } /** * Unescapes a string. * * @param string $text Text to unescape. * @param string $strategy Escape strategy. * * @return string * @throws InvalidArgumentException When unknown escape strategy is given. */ public static function unescape($text, $strategy = null) { if ( !isset($strategy) ) { $strategy = self::$escapeStrategy; } if ( strpos($strategy, '+') !== false ) { $previous_strategy = ''; $strategies = explode('+', $strategy); foreach ($strategies as $current_strategy) { // apply default strategy if ( $current_strategy == '' ) { $current_strategy = self::$escapeStrategy; } // don't double-unescape if ( $current_strategy != $previous_strategy ) { $text = self::unescape($text, $current_strategy); $previous_strategy = $current_strategy; } } return $text; } if ( $strategy == self::ESCAPE_HTML ) { return htmlspecialchars_decode($text, ENT_QUOTES); } if ( $strategy == self::ESCAPE_JS ) { // TODO: consider using "stripcslashes", because "stripslashes" isn't really for JavaScript unescaping (according to docs) $text = str_replace("", '', $text); $text = str_replace(array('\r', '\n'), array("\r", "\n"), $text); $text = stripslashes($text); return $text; } if ( $strategy == self::ESCAPE_URL ) { return rawurldecode($text); } if ( $strategy == self::ESCAPE_RAW ) { return $text; } throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); } + + /** + * Mark a method as deprecated and inform when it has been used. + * + * The current behavior is to trigger a user deprecation notice in Debug Mode. + * This method is to be used in every method that is deprecated. + * + * @param string $method The method that was called. + * @param string $version The version that deprecated the method. + * @param string|null $replacement The method that should have been called. + * + * @return void + */ + public static function deprecatedMethod($method, $version, $replacement = null) + { + $application =& kApplication::Instance(); + + if ( !$application->isDebugMode() ) { + return; + } + + $msg = '%1$s is deprecated since version %2$s'; + + if ( !is_null($replacement) ) { + @trigger_error(sprintf($msg . '! Use %3$s instead.', $method, $version, $replacement), E_USER_DEPRECATED); + } + else { + @trigger_error(sprintf($msg . ' with no alternative available.', $method, $version), E_USER_DEPRECATED); + } + } + + /** + * Mark a method argument as deprecated and inform when it has been used. + * + * This method is to be used whenever a deprecated method argument is used. + * Before this method is called, the argument must be checked for whether it was + * used by comparing it to its default value or evaluating whether it is empty. + * For example: + * + * if ( !$deprecated ) { + * kUtil::deprecatedArgument(__METHOD__, '5.2.2'); + * } + * + * The current behavior is to trigger a user deprecation notice in Debug Mode. + * + * @param string $method The method that was called. + * @param string $version The version that deprecated the argument used. + * @param string|null $message A message regarding the change. + * + * @return void + */ + public static function deprecatedArgument($method, $version, $message = null) + { + $application =& kApplication::Instance(); + + if ( !$application->isDebugMode() ) { + return; + } + + $msg = '%1$s was called with an argument that is deprecated since version %2$s'; + + if ( !is_null($message) ) { + @trigger_error(sprintf($msg . '! %3$s', $method, $version, $message), E_USER_DEPRECATED); + } + else { + @trigger_error(sprintf($msg . ' with no alternative available.', $method, $version), E_USER_DEPRECATED); + } + } + } /** * Returns array value if key exists * Accepts infinite number of parameters * * @param Array $array searchable array * @param int $key array key * @return string */ function getArrayValue(&$array, $key) { $ret = isset($array[$key]) ? $array[$key] : false; if ( $ret && func_num_args() > 2 ) { for ($i = 2; $i < func_num_args(); $i++) { $cur_key = func_get_arg($i); $ret = getArrayValue($ret, $cur_key); if ( $ret === false ) { break; } } } return $ret; } if ( !function_exists('parse_ini_string') ) { /** * Equivalent for "parse_ini_string" function available since PHP 5.3.0 * * @param string $ini * @param bool $process_sections * @param int $scanner_mode * @return Array */ function parse_ini_string($ini, $process_sections = false, $scanner_mode = NULL) { # Generate a temporary file. $tempname = tempnam('/tmp', 'ini'); $fp = fopen($tempname, 'w'); fwrite($fp, $ini); $ini = parse_ini_file($tempname, !empty($process_sections)); fclose($fp); @unlink($tempname); return $ini; } } if ( !function_exists('memory_get_usage') ) { // PHP 4.x and compiled without --enable-memory-limit option function memory_get_usage() { return -1; } } if ( !function_exists('imagecreatefrombmp') ) { // just in case if GD will add this function in future function imagecreatefrombmp($filename) { //Ouverture du fichier en mode binaire if (! $f1 = fopen($filename,"rb")) return FALSE; //1 : Chargement des ent�tes FICHIER $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14)); if ($FILE['file_type'] != 19778) return FALSE; //2 : Chargement des ent�tes BMP $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'. '/Vcompression/Vsize_bitmap/Vhoriz_resolution'. '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40)); $BMP['colors'] = pow(2,$BMP['bits_per_pixel']); if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset']; $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8; $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']); $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] = 4-(4*$BMP['decal']); if ($BMP['decal'] == 4) $BMP['decal'] = 0; //3 : Chargement des couleurs de la palette $PALETTE = array(); if ($BMP['colors'] < 16777216) { $PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4)); } //4 : Cr�ation de l'image $IMG = fread($f1,$BMP['size_bitmap']); $VIDE = chr(0); $res = imagecreatetruecolor($BMP['width'],$BMP['height']); $P = 0; $Y = $BMP['height']-1; while ($Y >= 0) { $X=0; while ($X < $BMP['width']) { if ($BMP['bits_per_pixel'] == 24) $COLOR = unpack("V",substr($IMG,$P,3).$VIDE); elseif ($BMP['bits_per_pixel'] == 16) { $COLOR = unpack("n",substr($IMG,$P,2)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 8) { $COLOR = unpack("n",$VIDE.substr($IMG,$P,1)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 4) { $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 1) { $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7; elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6; elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5; elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4; elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3; elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2; elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1; elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } else return FALSE; imagesetpixel($res,$X,$Y,$COLOR[1]); $X++; $P += $BMP['bytes_per_pixel']; } $Y--; $P+=$BMP['decal']; } //Fermeture du fichier fclose($f1); return $res; } }