PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/helpers/BaseVarDumper.php

https://gitlab.com/lcp0578/yii2
PHP | 262 lines | 185 code | 16 blank | 61 comment | 22 complexity | 221c7c97668a451bdde563229c7af6af MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\helpers;
  8. use yii\base\Arrayable;
  9. /**
  10. * BaseVarDumper provides concrete implementation for [[VarDumper]].
  11. *
  12. * Do not use BaseVarDumper. Use [[VarDumper]] instead.
  13. *
  14. * @author Qiang Xue <qiang.xue@gmail.com>
  15. * @since 2.0
  16. */
  17. class BaseVarDumper
  18. {
  19. private static $_objects;
  20. private static $_output;
  21. private static $_depth;
  22. /**
  23. * Displays a variable.
  24. * This method achieves the similar functionality as var_dump and print_r
  25. * but is more robust when handling complex objects such as Yii controllers.
  26. * @param mixed $var variable to be dumped
  27. * @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
  28. * @param boolean $highlight whether the result should be syntax-highlighted
  29. */
  30. public static function dump($var, $depth = 10, $highlight = false)
  31. {
  32. echo static::dumpAsString($var, $depth, $highlight);
  33. }
  34. /**
  35. * Dumps a variable in terms of a string.
  36. * This method achieves the similar functionality as var_dump and print_r
  37. * but is more robust when handling complex objects such as Yii controllers.
  38. * @param mixed $var variable to be dumped
  39. * @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
  40. * @param boolean $highlight whether the result should be syntax-highlighted
  41. * @return string the string representation of the variable
  42. */
  43. public static function dumpAsString($var, $depth = 10, $highlight = false)
  44. {
  45. self::$_output = '';
  46. self::$_objects = [];
  47. self::$_depth = $depth;
  48. self::dumpInternal($var, 0);
  49. if ($highlight) {
  50. $result = highlight_string("<?php\n" . self::$_output, true);
  51. self::$_output = preg_replace('/&lt;\\?php<br \\/>/', '', $result, 1);
  52. }
  53. return self::$_output;
  54. }
  55. /**
  56. * @param mixed $var variable to be dumped
  57. * @param integer $level depth level
  58. */
  59. private static function dumpInternal($var, $level)
  60. {
  61. switch (gettype($var)) {
  62. case 'boolean':
  63. self::$_output .= $var ? 'true' : 'false';
  64. break;
  65. case 'integer':
  66. self::$_output .= "$var";
  67. break;
  68. case 'double':
  69. self::$_output .= "$var";
  70. break;
  71. case 'string':
  72. self::$_output .= "'" . addslashes($var) . "'";
  73. break;
  74. case 'resource':
  75. self::$_output .= '{resource}';
  76. break;
  77. case 'NULL':
  78. self::$_output .= 'null';
  79. break;
  80. case 'unknown type':
  81. self::$_output .= '{unknown}';
  82. break;
  83. case 'array':
  84. if (self::$_depth <= $level) {
  85. self::$_output .= '[...]';
  86. } elseif (empty($var)) {
  87. self::$_output .= '[]';
  88. } else {
  89. $keys = array_keys($var);
  90. $spaces = str_repeat(' ', $level * 4);
  91. self::$_output .= '[';
  92. foreach ($keys as $key) {
  93. self::$_output .= "\n" . $spaces . ' ';
  94. self::dumpInternal($key, 0);
  95. self::$_output .= ' => ';
  96. self::dumpInternal($var[$key], $level + 1);
  97. }
  98. self::$_output .= "\n" . $spaces . ']';
  99. }
  100. break;
  101. case 'object':
  102. if (($id = array_search($var, self::$_objects, true)) !== false) {
  103. self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)';
  104. } elseif (self::$_depth <= $level) {
  105. self::$_output .= get_class($var) . '(...)';
  106. } else {
  107. $id = array_push(self::$_objects, $var);
  108. $className = get_class($var);
  109. $spaces = str_repeat(' ', $level * 4);
  110. self::$_output .= "$className#$id\n" . $spaces . '(';
  111. foreach ((array) $var as $key => $value) {
  112. $keyDisplay = strtr(trim($key), "\0", ':');
  113. self::$_output .= "\n" . $spaces . " [$keyDisplay] => ";
  114. self::dumpInternal($value, $level + 1);
  115. }
  116. self::$_output .= "\n" . $spaces . ')';
  117. }
  118. break;
  119. }
  120. }
  121. /**
  122. * Exports a variable as a string representation.
  123. *
  124. * The string is a valid PHP expression that can be evaluated by PHP parser
  125. * and the evaluation result will give back the variable value.
  126. *
  127. * This method is similar to `var_export()`. The main difference is that
  128. * it generates more compact string representation using short array syntax.
  129. *
  130. * It also handles objects by using the PHP functions serialize() and unserialize().
  131. *
  132. * PHP 5.4 or above is required to parse the exported value.
  133. *
  134. * @param mixed $var the variable to be exported.
  135. * @return string a string representation of the variable
  136. */
  137. public static function export($var)
  138. {
  139. self::$_output = '';
  140. self::exportInternal($var, 0);
  141. return self::$_output;
  142. }
  143. /**
  144. * @param mixed $var variable to be exported
  145. * @param integer $level depth level
  146. */
  147. private static function exportInternal($var, $level)
  148. {
  149. switch (gettype($var)) {
  150. case 'NULL':
  151. self::$_output .= 'null';
  152. break;
  153. case 'array':
  154. if (empty($var)) {
  155. self::$_output .= '[]';
  156. } else {
  157. $keys = array_keys($var);
  158. $outputKeys = ($keys !== range(0, count($var) - 1));
  159. $spaces = str_repeat(' ', $level * 4);
  160. self::$_output .= '[';
  161. foreach ($keys as $key) {
  162. self::$_output .= "\n" . $spaces . ' ';
  163. if ($outputKeys) {
  164. self::exportInternal($key, 0);
  165. self::$_output .= ' => ';
  166. }
  167. self::exportInternal($var[$key], $level + 1);
  168. self::$_output .= ',';
  169. }
  170. self::$_output .= "\n" . $spaces . ']';
  171. }
  172. break;
  173. case 'object':
  174. if ($var instanceof \Closure) {
  175. self::$_output .= self::exportClosure($var);
  176. } else {
  177. try {
  178. $output = 'unserialize(' . var_export(serialize($var), true) . ')';
  179. } catch (\Exception $e) {
  180. // serialize may fail, for example: if object contains a `\Closure` instance
  181. // so we use a fallback
  182. if ($var instanceof Arrayable) {
  183. self::exportInternal($var->toArray(), $level);
  184. return;
  185. } elseif ($var instanceof \IteratorAggregate) {
  186. $varAsArray = [];
  187. foreach ($var as $key => $value) {
  188. $varAsArray[$key] = $value;
  189. }
  190. self::exportInternal($varAsArray, $level);
  191. return;
  192. } elseif (method_exists($var, '__toString')) {
  193. $output = var_export($var->__toString(), true);
  194. } else {
  195. $outputBackup = self::$_output;
  196. $output = var_export(self::dumpAsString($var), true);
  197. self::$_output = $outputBackup;
  198. }
  199. }
  200. self::$_output .= $output;
  201. }
  202. break;
  203. default:
  204. self::$_output .= var_export($var, true);
  205. }
  206. }
  207. /**
  208. * Exports a [[Closure]] instance.
  209. * @param \Closure $closure closure instance.
  210. * @return string
  211. */
  212. private static function exportClosure(\Closure $closure)
  213. {
  214. $reflection = new \ReflectionFunction($closure);
  215. $fileName = $reflection->getFileName();
  216. $start = $reflection->getStartLine();
  217. $end = $reflection->getEndLine();
  218. if ($fileName === false || $start === false || $end === false) {
  219. return 'function() {/* Error: unable to determine Closure source */}';
  220. }
  221. --$start;
  222. $source = implode("\n", array_slice(file($fileName), $start, $end - $start));
  223. $tokens = token_get_all('<?php ' . $source);
  224. array_shift($tokens);
  225. $closureTokens = [];
  226. $pendingParenthesisCount = 0;
  227. foreach ($tokens as $token) {
  228. if (isset($token[0]) && $token[0] === T_FUNCTION) {
  229. $closureTokens[] = $token[1];
  230. continue;
  231. }
  232. if ($closureTokens !== []) {
  233. $closureTokens[] = isset($token[1]) ? $token[1] : $token;
  234. if ($token === '}') {
  235. $pendingParenthesisCount--;
  236. if ($pendingParenthesisCount === 0) {
  237. break;
  238. }
  239. } elseif ($token === '{') {
  240. $pendingParenthesisCount++;
  241. }
  242. }
  243. }
  244. return implode('', $closureTokens);
  245. }
  246. }