1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20:
21: final class NDebugHelpers
22: {
23:
24: 25: 26: 27:
28: public static function editorLink($file, $line)
29: {
30: if (NDebugger::$editor && is_file($file)) {
31: $dir = dirname(strtr($file, '/', DIRECTORY_SEPARATOR));
32: $base = isset($_SERVER['SCRIPT_FILENAME']) ? dirname(dirname(strtr($_SERVER['SCRIPT_FILENAME'], '/', DIRECTORY_SEPARATOR))) : dirname($dir);
33: if (substr($dir, 0, strlen($base)) === $base) {
34: $dir = '...' . substr($dir, strlen($base));
35: }
36: return NHtml::el('a')
37: ->href(strtr(NDebugger::$editor, array('%file' => rawurlencode($file), '%line' => $line)))
38: ->title("$file:$line")
39: ->setHtml(htmlSpecialChars(rtrim($dir, DIRECTORY_SEPARATOR)) . DIRECTORY_SEPARATOR . '<b>' . htmlSpecialChars(basename($file)) . '</b>');
40: } else {
41: return NHtml::el('span')->setText($file);
42: }
43: }
44:
45:
46:
47: 48: 49: 50: 51: 52:
53: public static function htmlDump(&$var, $level = 0)
54: {
55: static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
56: if ($tableUtf === NULL) {
57: foreach (range("\x00", "\xFF") as $ch) {
58: if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) {
59: $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
60: } elseif (ord($ch) < 127) {
61: $tableUtf[$ch] = $tableBin[$ch] = $ch;
62: } else {
63: $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch));
64: }
65: }
66: $tableBin["\\"] = '\\\\';
67: $tableBin["\r"] = '\\r';
68: $tableBin["\n"] = '\\n';
69: $tableBin["\t"] = '\\t';
70: $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
71: }
72:
73: if (is_bool($var)) {
74: return '<span class="php-bool">' . ($var ? 'TRUE' : 'FALSE') . "</span>\n";
75:
76: } elseif ($var === NULL) {
77: return "<span class=\"php-null\">NULL</span>\n";
78:
79: } elseif (is_int($var)) {
80: return "<span class=\"php-int\">$var</span>\n";
81:
82: } elseif (is_float($var)) {
83: $var = var_export($var, TRUE);
84: if (strpos($var, '.') === FALSE) {
85: $var .= '.0';
86: }
87: return "<span class=\"php-float\">$var</span>\n";
88:
89: } elseif (is_string($var)) {
90: if (NDebugger::$maxLen && strlen($var) > NDebugger::$maxLen) {
91: $s = htmlSpecialChars(substr($var, 0, NDebugger::$maxLen), ENT_NOQUOTES, 'ISO-8859-1') . ' ... ';
92: } else {
93: $s = htmlSpecialChars($var, ENT_NOQUOTES, 'ISO-8859-1');
94: }
95: $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf);
96: $len = strlen($var);
97: return "<span class=\"php-string\">\"$s\"</span>" . ($len > 1 ? " ($len)" : "") . "\n";
98:
99: } elseif (is_array($var)) {
100: $s = '<span class="php-array">array</span>(' . count($var) . ") ";
101: $space = str_repeat($space1 = ' ', $level);
102: $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}";
103:
104: static $marker;
105: if ($marker === NULL) {
106: $marker = uniqid("\x00", TRUE);
107: }
108: if (empty($var)) {
109:
110: } elseif (isset($var[$marker])) {
111: $brackets = $var[$marker];
112: $s .= "$brackets[0] *RECURSION* $brackets[1]";
113:
114: } elseif ($level < NDebugger::$maxDepth || !NDebugger::$maxDepth) {
115: $s .= "<code>$brackets[0]\n";
116: $var[$marker] = $brackets;
117: foreach ($var as $k => &$v) {
118: if ($k === $marker) {
119: continue;
120: }
121: $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
122: $k = htmlSpecialChars(preg_match('#^\w+$#', $k) ? $k : "\"$k\"");
123: $s .= "$space$space1<span class=\"php-key\">$k</span> => " . self::htmlDump($v, $level + 1);
124: }
125: unset($var[$marker]);
126: $s .= "$space$brackets[1]</code>";
127:
128: } else {
129: $s .= "$brackets[0] ... $brackets[1]";
130: }
131: return $s . "\n";
132:
133: } elseif (is_object($var)) {
134: if ($var instanceof Closure) {
135: $rc = new ReflectionFunction($var);
136: $arr = array();
137: foreach ($rc->getParameters() as $param) {
138: $arr[] = '$' . $param->getName();
139: }
140: $arr = array('file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'parameters' => implode(', ', $arr));
141: } else {
142: $arr = (array) $var;
143: }
144: $s = '<span class="php-object">' . get_class($var) . "</span>(" . count($arr) . ") ";
145: $space = str_repeat($space1 = ' ', $level);
146:
147: static $list = array();
148: if (empty($arr)) {
149:
150: } elseif (in_array($var, $list, TRUE)) {
151: $s .= "{ *RECURSION* }";
152:
153: } elseif ($level < NDebugger::$maxDepth || !NDebugger::$maxDepth || $var instanceof Closure) {
154: $s .= "<code>{\n";
155: $list[] = $var;
156: foreach ($arr as $k => &$v) {
157: $m = '';
158: if ($k[0] === "\x00") {
159: $m = ' <span class="php-visibility">' . ($k[1] === '*' ? 'protected' : 'private') . '</span>';
160: $k = substr($k, strrpos($k, "\x00") + 1);
161: }
162: $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
163: $k = htmlSpecialChars(preg_match('#^\w+$#', $k) ? $k : "\"$k\"");
164: $s .= "$space$space1<span class=\"php-key\">$k</span>$m => " . self::htmlDump($v, $level + 1);
165: }
166: array_pop($list);
167: $s .= "$space}</code>";
168:
169: } else {
170: $s .= "{ ... }";
171: }
172: return $s . "\n";
173:
174: } elseif (is_resource($var)) {
175: $type = get_resource_type($var);
176: $s = '<span class="php-resource">' . htmlSpecialChars($type) . " resource</span> ";
177:
178: static $info = array('stream' => 'stream_get_meta_data', 'curl' => 'curl_getinfo');
179: if (isset($info[$type])) {
180: $space = str_repeat($space1 = ' ', $level);
181: $s .= "<code>{\n";
182: foreach (call_user_func($info[$type], $var) as $k => $v) {
183: $s .= $space . $space1 . '<span class="php-key">' . htmlSpecialChars($k) . "</span> => " . self::htmlDump($v, $level + 1);
184: }
185: $s .= "$space}</code>";
186: }
187: return $s . "\n";
188:
189: } else {
190: return "<span>unknown type</span>\n";
191: }
192: }
193:
194:
195:
196: 197: 198: 199: 200:
201: public static function clickableDump($dump, $collapsed = FALSE)
202: {
203: return '<pre class="nette-dump">' . preg_replace_callback(
204: '#^( *)((?>[^(\r\n]{1,200}))\((\d+)\) <code>#m',
205: create_function('$m', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('collapsed'=>$collapsed)).'-1], EXTR_REFS);
206: return "$m[1]<a href=\'#\' rel=\'next\'>$m[2]($m[3]) "
207: . (($m[1] || !$collapsed) && ($m[3] < 7)
208: ? \'<abbr>▼</abbr> </a><code>\'
209: : \'<abbr>►</abbr> </a><code class="nette-collapsed">\');
210: '),
211: self::htmlDump($dump)
212: ) . '</pre>';
213: }
214:
215:
216:
217: public static function findTrace(array $trace, $method, & $index = NULL)
218: {
219: $m = explode('::', $method);
220: foreach ($trace as $i => $item) {
221: if (isset($item['function']) && $item['function'] === end($m)
222: && isset($item['class']) === isset($m[1])
223: && (!isset($item['class']) || $item['class'] === $m[0] || $m[0] === '*' || is_subclass_of($item['class'], $m[0]))
224: ) {
225: $index = $i;
226: return $item;
227: }
228: }
229: }
230:
231: }
232: