PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/source/class/vizto/vizto_debug.php

https://github.com/jinbo51/DiscuzX
PHP | 383 lines | 207 code | 61 blank | 115 comment | 43 complexity | e78e7b30543dfeaecd251680aaeeb28e MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php defined('IN_DISCUZ') || die('Access Denied');
  2. /**
  3. * Created by IntelliJ IDEA.
  4. * User: Administrator
  5. * Date: 13-5-6
  6. * Time: 上午7:39
  7. * To change this template use File | Settings | File Templates.
  8. */
  9. class vizto_debug {
  10. /**
  11. * Returns an HTML string of debugging information about any number of
  12. * variables, each wrapped in a "pre" tag:
  13. *
  14. * // Displays the type and value of each variable
  15. * echo Debug::vars($foo, $bar, $baz);
  16. *
  17. * @param mixed $var,... variable to debug
  18. *
  19. * @return string
  20. */
  21. public static function vars() {
  22. if(func_num_args() === 0)
  23. return;
  24. // Get all passed variables
  25. $variables = func_get_args();
  26. $output = array();
  27. foreach($variables as $var) {
  28. $output[] = vizto_debug::_dump($var, 1024);
  29. }
  30. return '<pre class="debug">'.implode("\n", $output).'</pre>';
  31. }
  32. /**
  33. * Returns an HTML string of information about a single variable.
  34. *
  35. * Borrows heavily on concepts from the Debug class of [Nette](http://nettephp.com/).
  36. *
  37. * @param mixed $value variable to dump
  38. * @param integer $length maximum length of strings
  39. * @param integer $level_recursion recursion limit
  40. *
  41. * @return string
  42. */
  43. public static function dump($value, $length = 128, $level_recursion = 10) {
  44. return vizto_debug::_dump($value, $length, $level_recursion);
  45. }
  46. /**
  47. * Helper for Debug::dump(), handles recursion in arrays and objects.
  48. *
  49. * @param mixed $var variable to dump
  50. * @param integer $length maximum length of strings
  51. * @param integer $limit recursion limit
  52. * @param integer $level current recursion level (internal usage only!)
  53. *
  54. * @return string
  55. */
  56. protected static function _dump(& $var, $length = 128, $limit = 10, $level = 0) {
  57. global $_G;
  58. if($var === NULL) {
  59. return '<small>NULL</small>';
  60. } elseif(is_bool($var)) {
  61. return '<small>bool</small> '.($var ? 'TRUE' : 'FALSE');
  62. } elseif(is_float($var)) {
  63. return '<small>float</small> '.$var;
  64. } elseif(is_resource($var)) {
  65. if(($type = get_resource_type($var)) === 'stream' AND $meta = stream_get_meta_data($var)) {
  66. $meta = stream_get_meta_data($var);
  67. if(isset($meta['uri'])) {
  68. $file = $meta['uri'];
  69. if(function_exists('stream_is_local')) {
  70. // Only exists on PHP >= 5.2.4
  71. if(stream_is_local($file)) {
  72. $file = Debug::path($file);
  73. }
  74. }
  75. return '<small>resource</small><span>('.$type.')</span> '.htmlspecialchars($file, ENT_NOQUOTES, Kohana::$charset);
  76. }
  77. } else {
  78. return '<small>resource</small><span>('.$type.')</span>';
  79. }
  80. } elseif(is_string($var)) {
  81. // Clean invalid multibyte characters. iconv is only invoked
  82. // if there are non ASCII characters in the string, so this
  83. // isn't too much of a hit.
  84. $var = UTF8::clean($var, Kohana::$charset);
  85. if(UTF8::strlen($var) > $length) {
  86. // Encode the truncated string
  87. $str = htmlspecialchars(UTF8::substr($var, 0, $length), ENT_NOQUOTES, $_G['charset']).'&nbsp;&hellip;';
  88. } else {
  89. // Encode the string
  90. $str = htmlspecialchars($var, ENT_NOQUOTES, $_G['charset']);
  91. }
  92. return '<small>string</small><span>('.strlen($var).')</span> "'.$str.'"';
  93. } elseif(is_array($var)) {
  94. $output = array();
  95. // Indentation for this variable
  96. $space = str_repeat($s = ' ', $level);
  97. static $marker;
  98. if($marker === NULL) {
  99. // Make a unique marker
  100. $marker = uniqid("\x00");
  101. }
  102. if(empty($var)) {
  103. // Do nothing
  104. } elseif(isset($var[$marker])) {
  105. $output[] = "(\n$space$s*RECURSION*\n$space)";
  106. } elseif($level < $limit) {
  107. $output[] = "<span>(";
  108. $var[$marker] = TRUE;
  109. foreach($var as $key => & $val) {
  110. if($key === $marker) continue;
  111. if(!is_int($key)) {
  112. $key = '"'.htmlspecialchars($key, ENT_NOQUOTES, Kohana::$charset).'"';
  113. }
  114. $output[] = "$space$s$key => ".vizto_debug::_dump($val, $length, $limit, $level + 1);
  115. }
  116. unset($var[$marker]);
  117. $output[] = "$space)</span>";
  118. } else {
  119. // Depth too great
  120. $output[] = "(\n$space$s...\n$space)";
  121. }
  122. return '<small>array</small><span>('.count($var).')</span> '.implode("\n", $output);
  123. } elseif(is_object($var)) {
  124. // Copy the object as an array
  125. $array = (array)$var;
  126. $output = array();
  127. // Indentation for this variable
  128. $space = str_repeat($s = ' ', $level);
  129. $hash = spl_object_hash($var);
  130. // Objects that are being dumped
  131. static $objects = array();
  132. if(empty($var)) {
  133. // Do nothing
  134. } elseif(isset($objects[$hash])) {
  135. $output[] = "{\n$space$s*RECURSION*\n$space}";
  136. } elseif($level < $limit) {
  137. $output[] = "<code>{";
  138. $objects[$hash] = TRUE;
  139. foreach($array as $key => & $val) {
  140. if($key[0] === "\x00") {
  141. // Determine if the access is protected or protected
  142. $access = '<small>'.(($key[1] === '*') ? 'protected' : 'private').'</small>';
  143. // Remove the access level from the variable name
  144. $key = substr($key, strrpos($key, "\x00") + 1);
  145. } else {
  146. $access = '<small>public</small>';
  147. }
  148. $output[] = "$space$s$access $key => ".vizto_debug::_dump($val, $length, $limit, $level + 1);
  149. }
  150. unset($objects[$hash]);
  151. $output[] = "$space}</code>";
  152. } else {
  153. // Depth too great
  154. $output[] = "{\n$space$s...\n$space}";
  155. }
  156. return '<small>object</small> <span>'.get_class($var).'('.count($array).')</span> '.implode("\n", $output);
  157. } else {
  158. return '<small>'.gettype($var).'</small> '.htmlspecialchars(print_r($var, TRUE), ENT_NOQUOTES, $_G['charset']);
  159. }
  160. }
  161. /**
  162. * Removes application, system, modpath, or docroot from a filename,
  163. * replacing them with the plain text equivalents. Useful for debugging
  164. * when you want to display a shorter path.
  165. *
  166. * // Displays SYSPATH/classes/kohana.php
  167. * echo Debug::path(Kohana::find_file('classes', 'kohana'));
  168. *
  169. * @param string $file path to debug
  170. *
  171. * @return string
  172. */
  173. public static function path($file) {
  174. if(strpos($file, APPPATH) === 0) {
  175. $file = 'APPPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(APPPATH));
  176. } elseif(strpos($file, SYSPATH) === 0) {
  177. $file = 'SYSPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(SYSPATH));
  178. } elseif(strpos($file, MODPATH) === 0) {
  179. $file = 'MODPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(MODPATH));
  180. } elseif(strpos($file, DOCROOT) === 0) {
  181. $file = 'DOCROOT'.DIRECTORY_SEPARATOR.substr($file, strlen(DOCROOT));
  182. }
  183. return $file;
  184. }
  185. /**
  186. * Returns an HTML string, highlighting a specific line of a file, with some
  187. * number of lines padded above and below.
  188. *
  189. * // Highlights the current line of the current file
  190. * echo Debug::source(__FILE__, __LINE__);
  191. *
  192. * @param string $file file to open
  193. * @param integer $line_number line number to highlight
  194. * @param integer $padding number of padding lines
  195. *
  196. * @return string source of file
  197. * @return FALSE file is unreadable
  198. */
  199. public static function source($file, $line_number, $padding = 5) {
  200. global $_G;
  201. if(!$file OR !is_readable($file)) {
  202. // Continuing will cause errors
  203. return FALSE;
  204. }
  205. // Open the file and set the line position
  206. $file = fopen($file, 'r');
  207. $line = 0;
  208. // Set the reading range
  209. $range = array('start' => $line_number - $padding, 'end' => $line_number + $padding);
  210. // Set the zero-padding amount for line numbers
  211. $format = '% '.strlen($range['end']).'d';
  212. $source = '';
  213. while(($row = fgets($file)) !== FALSE) {
  214. // Increment the line number
  215. if(++$line > $range['end'])
  216. break;
  217. if($line >= $range['start']) {
  218. // Make the row safe for output
  219. $row = htmlspecialchars($row, ENT_NOQUOTES, $_G['charset']);
  220. // Trim whitespace and sanitize the row
  221. $row = '<span class="number">'.sprintf($format, $line).'</span> '.$row;
  222. if($line === $line_number) {
  223. // Apply highlighting to this row
  224. $row = '<span class="line highlight">'.$row.'</span>';
  225. } else {
  226. $row = '<span class="line">'.$row.'</span>';
  227. }
  228. // Add to the captured source
  229. $source .= $row;
  230. }
  231. }
  232. // Close the file
  233. fclose($file);
  234. return '<pre class="source"><code>'.$source.'</code></pre>';
  235. }
  236. /**
  237. * Returns an array of HTML strings that represent each step in the backtrace.
  238. *
  239. * // Displays the entire current backtrace
  240. * echo implode('<br/>', Debug::trace());
  241. *
  242. * @param array $trace
  243. *
  244. * @return string
  245. */
  246. public static function trace(array $trace = NULL) {
  247. if($trace === NULL) {
  248. // Start a new trace
  249. $trace = debug_backtrace();
  250. }
  251. // Non-standard function calls
  252. $statements = array('include', 'include_once', 'require', 'require_once');
  253. $output = array();
  254. foreach($trace as $step) {
  255. if(!isset($step['function'])) {
  256. // Invalid trace step
  257. continue;
  258. }
  259. if(isset($step['file']) AND isset($step['line'])) {
  260. // Include the source of this step
  261. $source = vizto_debug::source($step['file'], $step['line']);
  262. }
  263. if(isset($step['file'])) {
  264. $file = $step['file'];
  265. if(isset($step['line'])) {
  266. $line = $step['line'];
  267. }
  268. }
  269. // function()
  270. $function = $step['function'];
  271. if(in_array($step['function'], $statements)) {
  272. if(empty($step['args'])) {
  273. // No arguments
  274. $args = array();
  275. } else {
  276. // Sanitize the file path
  277. $args = array($step['args'][0]);
  278. }
  279. } elseif(isset($step['args'])) {
  280. if(!function_exists($step['function']) OR strpos($step['function'], '{closure}') !== FALSE) {
  281. // Introspection on closures or language constructs in a stack trace is impossible
  282. $params = NULL;
  283. } else {
  284. if(isset($step['class'])) {
  285. if(method_exists($step['class'], $step['function'])) {
  286. $reflection = new ReflectionMethod($step['class'], $step['function']);
  287. } else {
  288. $reflection = new ReflectionMethod($step['class'], '__call');
  289. }
  290. } else {
  291. $reflection = new ReflectionFunction($step['function']);
  292. }
  293. // Get the function parameters
  294. $params = $reflection->getParameters();
  295. }
  296. $args = array();
  297. foreach($step['args'] as $i => $arg) {
  298. if(isset($params[$i])) {
  299. // Assign the argument by the parameter name
  300. $args[$params[$i]->name] = $arg;
  301. } else {
  302. // Assign the argument by number
  303. $args[$i] = $arg;
  304. }
  305. }
  306. }
  307. if(isset($step['class'])) {
  308. // Class->method() or Class::method()
  309. $function = $step['class'].$step['type'].$step['function'];
  310. }
  311. $output[] = array(
  312. 'function' => $function,
  313. 'args' => isset($args) ? $args : NULL,
  314. 'file' => isset($file) ? $file : NULL,
  315. 'line' => isset($line) ? $line : NULL,
  316. 'source' => isset($source) ? $source : NULL,
  317. );
  318. unset($function, $args, $file, $line, $source);
  319. }
  320. return $output;
  321. }
  322. }