. ** ** debug.class - Debug Functionality for ITools */ /** * Debug functions */ class it_debug { var $level; static $force_html; /** * Constructor * @param $level Debug level. All debug() calls with a level <= $level are shown. */ function __construct($level=0) { $this->level = isset($GLOBALS['debug_level']) ? $GLOBALS['debug_level'] : $level; } /** * Output a message if the global debug level is higher or the same as $level * @param $text Message to display * @param $level Debug level */ function debug($text, $level = 0) { if ($this->level >= $level) { echo (isset($_SERVER['REMOTE_ADDR']) ? nl2br("$level $text\n") : "$level $text")."\n"; flush(); } } static function echo($args) { if (getenv('IT_ED_STDERR')) fputs(STDERR, self::dump($args)); else echo self::dump($args); } static function debug_backtrace($p = []) { if (!is_array($p)) $p = array('skiplevels' => $p); $p += array('levels' => 0, 'skiplevels'=> 0, 'skipfiles' => "###"); foreach ($p['trace'] ?: debug_backtrace(@constant('DEBUG_BACKTRACE_IGNORE_ARGS')) as $call) { if (!$result && it::match('it_debug\.class$', $call['file'])) continue; if (!$result && it::match($p['skipfiles'], $call['file'])) continue; if (!$result && $p['skiplevels']-- > 0) continue; $result[] = $call; if (--$p['levels'] == 0) break; } return (array)$result; } /** * Get source line of grandparent calling this function * @param $stacksoffs go up an extra $stacksoffs levels */ static function srcline($p = array()) { $stack = self::debug_backtrace($p); $line = $stack[1]['line']; $file = $stack[1]['file']; if (!isset($GLOBALS['it_debug::dump source'][$file])) $GLOBALS['it_debug::dump source'][$file] = @it::file($file); return $GLOBALS['it_debug::dump source'][$file][$line-1]; } /** * Prepare for debug output being echoed */ static function setup() { $GLOBALS['debug_noredir'] = 1; if (ob_get_level() == 0 && $_SERVER['REMOTE_ADDR']) ob_start(); # prevent later 'headers already sent' error if ($_SERVER['REMOTE_ADDR'] && !EDC('edplain') && it::match('(?:^|,)\s*text/html\s*(?:,|$)', $_SERVER['HTTP_ACCEPT'])) self::$force_html = true; } /** * Backend for functions D(), ED() and EDX() in functions.php * @param Origargs Array containing original arguments do ED() etc * @param $p['color'] Allow color [true] * @param $p['html'] Allow html [default based on REMOTE_ADDR] * @param $p['short'] Omit variable names * @return String representation of dump */ static function dump($args, $p = []) { $p += ['html' => !EDC('edplain') && (self::$force_html || $_SERVER['REMOTE_ADDR']), 'color' => true]; $htmlspecialchars = array("&" => "&", '<' => "<", '>' => ">"); $args = count($args) ? $args : [it_debug::backtrace(['skiplevels' => 1])]; if ((preg_match('/csv|txt|gif|jpg/', $_SERVER['PHP_SELF']) || preg_grep('#text/(calendar|css|javascript|json|plain|rfc822|xml)|application/#', headers_list()) || $GLOBALS['debug_edplain']) && !$GLOBALS['debug_edhtml']) ; else if ($p['html']) list($blue, $noblue) = $p['color'] ? ["", ""] : []; else if (defined('STDOUT') && posix_isatty(STDOUT) && $p['color']) list($blue, $noblue, $red, $nored, $ansiok) = getenv('IT_ED_BRIGHT') ? array("\033[34m", "\033[m", "\033[33m", "\033[m", 1) : array("\033[34m", "\033[m", "\033[31m", "\033[m", 1); $src = it_debug::srcline(); list($dummy, $function, $paramlist) = preg_match('#\b(D|ED|EDC|EDX)\s*\((.*)#i', $src, $parts) ? $parts : array(); $paramtokens = token_get_all("\s*([^\n]+),#", 'var \$$1 = $2;', $values); $item = rtrim($head, '\\') . "class $classname {" . $values . "}" . $tail; } $item = preg_replace_callback('/\b(1[2-9]\d\d\d\d\d\d\d\d)\b(.*)/', function($m) { return $m[1] . $m[2] . " # " . date('Y-m-d H:i:s', $m[1]); }, $item); $item = preg_replace("#(=>?)\s*\n\s*(array|class)#", '$1 $2', $item); # array( and class on same line as key $item = preg_replace_callback('#array \(\s+([^(){};]{1,100}),\s+\)#', function($m) { return "array (" . preg_replace("#\n\s+#", " ", $m[1]) . ")"; }, $item); # short arrays on 1 line $item = preg_replace('#class (\S+) \{\s+([^({,;]+;)?\s+\}#', 'class $1 { $2 }', $item); # 1-element objects on 1 line #$item = preg_replace('#\{\s*var \$attr#', '{ var $attr', $item); # move $attr on same line $item = preg_replace("#\\(\s*\n\s*\\)#", "()", $item); # empty arrays on 1 line $item = preg_replace("# =>\s*\n\s+\(object\) array\(\n#", " => (object) array(\n", $item); #$item = preg_replace('#\s+var \$_(.|\n)*?;\s*\n#', "", $item); $item = "$red$item$nored"; if ($p['html']) $item = it::replace(['[\x00-\x08\x0b-\x0c\x0e-\x1f]' => ""], preg_replace("#(https?:)?//[^\s'\"]+#", "\$0", strtr($item, $htmlspecialchars)), ['utf8' => false]); # htmlspecialchars() does not like bad utf8 if (preg_match('/^[\'"]/', $var)) $r .= "$item "; else { $var = $p['html'] ? strtr($var, $htmlspecialchars) : $var; $var = $p['short'] ? "" : "$blue$var=$noblue"; $r .= $previtem && preg_match("/\n/", $item . $previtem) ? "\n$var$item " : "$var$item "; $previtem = $item; } } if ($GLOBALS['debug_indent']) $r = str_repeat(" ", count(debug_backtrace(@constant('DEBUG_BACKTRACE_IGNORE_ARGS')))-3) . $r; # not it_html::Q(); we dont wanna load it_html in ultraclassloader debugging $title = htmlspecialchars(it_debug::backtrace(array('skiplevels' => 1)), ENT_COMPAT, "ISO-8859-1"); # skip this func as well as ED()/EDX() if ($p['html']) return "
$r
\n"; else return $ansiok || $p['short'] || EDC('edplain') ? "$r\n" : "\u{2588}\u{258c}" . preg_replace("#\n#", "\n\u{2588} ", "$r") . "\n"; } /** * Return short stackdump * @param $p['levels'] number of stack levels to return (default: 0 = all) * @param $p['skiplevels'] number of stack levels to omit * @param $p['skipfiles'] regular expression of filenames to omit * @param $p['trace'] Stack trace to compact */ static function backtrace($p = array()) { $frames = $p['trace'] ?? ($p['format'] ? array_slice(debug_backtrace(0), $p['skiplevels']) : null); if ($p['format'] == "long") { foreach ($frames as $frame) if ($tracesize < 100000 && ($tracesize += strlen(print_r($frame, true))) < 100000) # save mem $outframes[] = $frame; $result = $outframes ? print_r($outframes, true) . "\n" : null; } else if ($p['format'] == "medium") { foreach ($frames as $frame) { $locations[] = basename($frame['file']) . ":" . $frame['line']; $argstrings = []; foreach ((array)$frame['args'] as $arg) { $t = trim(it::replace(['^array \(\s*' => "[", ',\n\)$' => "]", '^array \( \)' => "[]", " +" => " ", "\n" => ""], var_export($arg, true)), " ,"); $argstrings[] = strlen($t) < 60 ? $t : it::replace(["^(.{0,60} |.{0,60}).*" => '$1'], $t) . "..." . (is_array($arg) ? "]" : (is_object($arg) ? "}" :"")); } $allargs = join(", ", $argstrings); if ($frame['type'] == "->" || $frame['type'] == "::") $funcs[] = $frame['class'] . $frame['type'] . $frame['function'] . "($allargs)"; else $funcs[] = $frame['function'] ? $frame['function'] . "($allargs)" : "[inline]"; $maxlen = max(it::map('strlen', $locations)); } foreach ($locations as $idx => $location) $result .= str_pad($location, $maxlen + 2) . $funcs[$idx] . "\n"; } else { foreach (self::debug_backtrace($p) as $frame) { $fn = $frame['file']; $fn = (it::match('auto_prepend', $fn) ? basename(dirname(dirname($fn))) . "/" : (it::match('/(cgi|bin)/', $fn) ? basename(dirname($fn)) . "/" : "")) . basename($fn); if ($fn) $result[] = $fn . ":" . $frame['line']; } $result = implode(" ", (array)$result); } return $result; } static function set($debug_string) { foreach (preg_split('/[.,]/', $debug_string) as $ultrad) { $ultravar = preg_split('/[-=:]/', $ultrad, 2); $GLOBALS["debug_$ultravar[0]"] = isset($ultravar[1]) ? $ultravar[1] : 1; } # keep debug parameters in subrequests $_SERVER['ULTRAHOSTNAME'] = it::replace(['^\.+' => '', '([^.])\.*$' => '\1.'], $debug_string) . $_SERVER['ULTRAHOSTNAME']; } } ?>