api.ic.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <?php
  2. /**
  3. * IceCream - Produces fast and clear debug output.
  4. *
  5. * ic() is like print(), but better:
  6. *
  7. * - It prints both expressions/variable/methods/functions names and their values.
  8. * - It's 60% faster to type.
  9. * - Data structures are pretty printed.
  10. * - It optionally includes program context: filename, line number, and parent function.
  11. *
  12. * More details: https://github.com/gruns/icecream
  13. *
  14. * @param mixed ...$values
  15. *
  16. * @return mixed
  17. */
  18. function ic(...$values) {
  19. $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
  20. $caller = $backtrace[0];
  21. $inside = $backtrace[1] ? $backtrace[1] : null;
  22. $fileName = basename($caller['file']);
  23. $fileLine = $caller['line'];
  24. $titlePrefix = 'ic | ';
  25. $outputFunction = (function_exists('show_window')) ? 'show_window' : '';
  26. if ($values === array()) {
  27. $string = basename($caller['file']) . ":{$caller['line']}";
  28. if (isset($inside['class'])) {
  29. $class = strpos($inside['class'], 'class@') === 0 ? 'class@anonymous' : $inside['class'];
  30. $string .= " in {$class}{$inside['type']}{$inside['function']}()";
  31. } elseif (isset($inside['function'])) {
  32. $string .= " in {$inside['function']}()";
  33. }
  34. $microtime = microtime();
  35. $microtime = explode(' ', $microtime);
  36. $string .= ' at ' . date("H:i:s") . '.' . round($microtime[0] * 1000);
  37. if ($outputFunction) {
  38. $outputFunction($titlePrefix . $fileName . ' line ' . $fileLine, $string);
  39. } else {
  40. print($titlePrefix . $fileName . ' line ' . $fileLine . PHP_EOL . $string . PHP_EOL);
  41. }
  42. return (null);
  43. }
  44. $fileContent = file_get_contents($caller['file']);
  45. $tokens = token_get_all($fileContent, TOKEN_PARSE);
  46. $tokenCount = count($tokens);
  47. $functionNameIndex = null;
  48. $functionUsageIndexes = array();
  49. // STEP 1: Find the function name
  50. // e.g. ic('foo')
  51. // ^^
  52. // The first token will always be the opening tag, or HTML before the opening tag, so we can safely skip it
  53. for ($i = 1; $i < $tokenCount; ++$i) {
  54. $token = $tokens[$i];
  55. if (! is_array($token)) {
  56. continue;
  57. }
  58. if ($token[2] > $caller['line']) {
  59. break;
  60. }
  61. if ($token[0] === T_STRING && strtolower($token[1]) === 'ic') {
  62. $functionUsageIndexes[] = $i;
  63. }
  64. }
  65. $functionNameIndex = end($functionUsageIndexes);
  66. $openBraceIndex = null;
  67. // STEP 2: Find the function call opening brace
  68. // e.g. ic('foo')
  69. // ^
  70. for ($i = $functionNameIndex + 1; $i < $tokenCount; ++$i) {
  71. if ($tokens[$i] === '(') {
  72. $openBraceIndex = $i;
  73. break;
  74. }
  75. }
  76. $depth = 0;
  77. $contents = array('');
  78. $current = 0;
  79. // STEP 3: Find all the tokens between the opening brace and the closing brace
  80. // e.g. ic('foo')
  81. // ^^^^^
  82. for ($i = $openBraceIndex + 1; $i < $tokenCount; ++$i) {
  83. $token = $tokens[$i];
  84. if ($token === '[' || $token === '{' || $token === '(') {
  85. ++$depth;
  86. }
  87. if ($token === ']' || $token === '}') {
  88. --$depth;
  89. }
  90. if ($token === ')') {
  91. if ($depth === 0) {
  92. break;
  93. }
  94. --$depth;
  95. }
  96. if ($depth === 0 && $token === ',') {
  97. ++$current;
  98. $contents[$current] = '';
  99. continue;
  100. }
  101. if (! is_array($token)) {
  102. $contents[$current] .= $token;
  103. continue;
  104. }
  105. $type = $token[0];
  106. if ($type === T_COMMENT || $type === T_DOC_COMMENT) {
  107. continue;
  108. }
  109. if ($type === T_WHITESPACE) {
  110. $contents[$current] .= ' ';
  111. continue;
  112. }
  113. $contents[$current] .= $token[1];
  114. }
  115. $strings = array();
  116. foreach ($contents as $i => $content) {
  117. $strings[] = trim($content) . ': ' . trim(print_r($values[$i], true));
  118. }
  119. if ($outputFunction) {
  120. $outputFunction($titlePrefix . $fileName . ' line ' . $fileLine, '<pre>' . print_r(implode(', ', $strings), true) . '</pre>');
  121. } else {
  122. print($titlePrefix . $fileName . ' line ' . $fileLine . PHP_EOL . print_r(implode(', ', $strings), true) . PHP_EOL);
  123. }
  124. $result = (count($values) === 1) ? $values[0] : $values;
  125. return ($result);
  126. }