$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 .= '
'.$args.'
';
							}
							$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;
				}
			}
			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 .= '';
							}
							$explain_table .= '| ' . implode(' | ', $explain_row) . ' | 
';
						}
						$formatted_sql .= '';
					}
				}
				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();
				?>
				
					
						| Src | Name | Value$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 ' | | '.$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'];
					if (array_key_exists('object', $trace_results[$i + 1]) && isset($trace_results[$i + 1]['object']->Prefix)) {
						$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');
			}
			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]);
					if ($func_arguments[6]) {
						$this->profilerAddTotal('cachable_queries', $key);
						$this->ProfilerData[$key]['subtitle'] = 'cachable';
					}
					if (array_key_exists('prefix_special', $this->ProfilerData[$key])) {
						$additional[] =	Array ('name' => 'PrefixSpecial', 'value' => $this->ProfilerData[$key]['prefix_special']);
					}
					$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 = '';
				$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
					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 ($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 '
';
			}
			/**
			 * 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 ($errorType == 'Warning') {
					$this->WarningCount++;
				}
				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 = '';
				return $html;
			}
			/**
			 * Returns HTML for dom viewer section
			 *
			 * @return string
			 */
			function _getDomViewerHTML()
			{
				$html = '';
				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') );
		}
	}