InitDebugger();
$this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4', $start);
$this->profileStart('script_runtime', 'Script runtime', $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();
}
/**
* Set's default values to constants debugger uses
*
*/
function InitDebugger()
{
unset($_REQUEST['debug_host'], $_REQUEST['debug_fastfile']); // this var messed up whole detection stuff :(
// Detect fact, that this session beeing debugged by Zend Studio
foreach ($_REQUEST as $rq_name => $rq_value) {
if (substr($rq_name, 0, 6) == 'debug_') {
$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';
}
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 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).'@';
// 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 = defined('WRITEABLE') ? WRITEABLE.'/cache' : FULL_PATH.'/kernel/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()
{
$dumpVars = func_get_args();
foreach ($dumpVars as $varValue) {
$this->Data[] = Array('value' => $varValue, 'debug_type' => 'var_dump');
}
}
function prepareHTML($dataIndex)
{
$Data =& $this->Data[$dataIndex];
if ($Data['debug_type'] == 'html') {
return $Data['html'];
}
switch ($Data['debug_type']) {
case 'error':
$fileLink = $this->getFileLink($Data['file'], $Data['line']);
$ret = ''.$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 '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, 50, 200));
$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);
$ret = 'Name: '.$Data['description'].' ';
if (isset($Data['file'])) {
$ret .= '[Runtime: '.$runtime.'s] [File: '.$Data['file'].'] ';
}
else {
$ret .= 'Runtime: '.$runtime.'s ';
}
$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',
'TemplateParser',
);
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 $cut_first cut first N symbols, don't cut anything if -1 specified
* @package int $cut_min_length cut only of this length of string or greather
* @return string
*/
function print_r(&$array, $return_output = false, $cut_first = -1, $cut_min_length = -1)
{
static $first_line = true, $tab_count = -1;
if (is_null($array)) {
return 'NULL';
}elseif (!is_array($array)) {
if ($cut_min_length != -1 && strlen($array) > $cut_min_length) {
$array = substr($array, 0, $cut_first).' ...';
}
return $array;
}
$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, 50, 200);
break;
case 'boolean':
$output .= $tabsign.'['.$key.'] = '.($array[$key] ? 'true' : 'false')."\n";
break;
case 'integer':
case 'double':
case 'string':
if ($cut_min_length != -1 && strlen($array[$key]) > $cut_min_length) {
$array[$key] = substr($array[$key], 0, $cut_first).' ...';
}
$output .= $tabsign.'['.$key.'] = '.$array[$key]."\n";
break;
case 'NULL':
$output .= $tabsign.'['.$key."] = NULL\n";
break;
case 'object':
$attribute_names = get_class_vars( get_class($array[$key]) );
if (!$attribute_names) {
$output .= $tabsign.'['.$key."] = NO_ATTRIBUTES\n";
}
else {
if ($this->IsBigObject($array[$key])) {
$output .= $tabsign.'['.$key.'] = SKIPPED (class: '.get_class($array[$key]).")\n";
break;
}
// $attribute_value - default value for this attribute, not used here
foreach ($attribute_names as $attribute_name => $attribute_value) {
if (is_object($array[$key]->$attribute_name)) {
// it is object
$object_class = get_class($array[$key]->$attribute_name);
if (!in_array($object_class, $this->RecursionStack)) {
// object [not in recursion stack]
if ($this->IsBigObject($array[$key]->$attribute_name)) {
$output .= $tabsign.'['.$attribute_name.'] = SKIPPED (class: '.$object_class.")\n";
continue;
}
array_push($this->RecursionStack, $object_class);
$output .= $this->print_r($array[$key]->$attribute_name, true, 50, 200);
array_pop($this->RecursionStack);
}
else {
// object [in recursion stack]
$output .= $tabsign.'['.$attribute_name.'] = *** RECURSION *** (class: '.$object_class.")\n";
}
}
else {
$output .= $tabsign.'['.$attribute_name.'] = '.$this->print_r($array[$key]->$attribute_name, true, 50, 200)."\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 only first 200 chars of string, this allow to save amount of data sent to browser
*
* @param string $string
* @return string
*/
function cutStringForHTML($string)
{
if (strlen($string) > 200) {
$string = substr($string,0,50).' ...';
}
return $string;
}
/**
* Format SQL Query using predefined formatting
* and highlighting techniques
*
* @param string $sql
* @return string
*/
function formatSQL($sql)
{
$sql = preg_replace('/(\n|\t| )+/is', ' ', $sql);
$sql = preg_replace('/(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY) /is', "\n\t$1 ", $sql);
return $this->highlightString($sql);
}
function highlightString($string)
{
if (!$this->constOn('DBG_USE_HIGHLIGHT')) {
return $string;
}
$string = str_replace( Array('\\', '/') , Array('_no_match_string_', '_n_m_s_'), $string);
$string = highlight_string('', true);
$string = str_replace( Array('_no_match_string_', '_n_m_s_'), Array('\\', '/'), $string);
return preg_replace('/<\?(.*)php(.*)\?>/Us', '\\2', $string);
}
/**
* Determine by php type of browser used to show debugger
*
* @return bool
*/
function isGecko()
{
return 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 (isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == 'yes') {
$this->appendHTML('RequestURI: '.$_SERVER['REQUEST_URI'].' (QS Length:'.strlen($_SERVER['QUERY_STRING']).')');
}
$this->appendHTML('DomViewer: ');
ob_start();
?>