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