From c51d090d2804cd7d8b09e71e60bac993156a4ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Thu, 10 Apr 2025 18:09:49 +0800 Subject: [PATCH] =?UTF-8?q?dd=20=E8=B0=83=E8=AF=95=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common.php | 18 ++ Server/application/common/helper/Debug.php | 354 +++++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 Server/application/common/helper/Debug.php diff --git a/Server/application/common.php b/Server/application/common.php index c1361ea5..cad1de58 100644 --- a/Server/application/common.php +++ b/Server/application/common.php @@ -509,3 +509,21 @@ if (!function_exists('exit_data')) { exit(); } } + +/** + * 调试打印变量并终止程序 + * @return void + */ +function dd() +{ + call_user_func_array(['app\\common\\helper\\Debug', 'dd'], func_get_args()); +} + +/** + * 调试打印变量但不终止程序 + * @return void + */ +function dump() +{ + call_user_func_array(['app\\common\\helper\\Debug', 'dump'], func_get_args()); +} diff --git a/Server/application/common/helper/Debug.php b/Server/application/common/helper/Debug.php new file mode 100644 index 00000000..bb824d42 --- /dev/null +++ b/Server/application/common/helper/Debug.php @@ -0,0 +1,354 @@ +调试输出'; + echo ''; + echo ''; + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $file = isset($trace[0]['file']) ? $trace[0]['file'] : '未知文件'; + $line = isset($trace[0]['line']) ? $trace[0]['line'] : '未知行号'; + + echo '
调试输出 - ' . $file . ' (第 ' . $line . ' 行)
'; + + foreach ($args as $index => $arg) { + echo '
'; + echo '

变量 #' . ($index + 1) . '

'; + echo '
' . self::formatVar($arg) . '
'; + echo '
'; + } + + // 打印调用栈 + echo '

调用栈

'; + echo '
'; + $traces = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); + foreach ($traces as $i => $t) { + if ($i === 0) continue; // 跳过当前函数 + $class = isset($t['class']) ? $t['class'] : ''; + $type = isset($t['type']) ? $t['type'] : ''; + $function = isset($t['function']) ? $t['function'] : ''; + $file = isset($t['file']) ? $t['file'] : '未知文件'; + $line = isset($t['line']) ? $t['line'] : '未知行号'; + + echo '
'; + echo '#' . $i . ' '; + echo $file . ' (' . $line . '): '; + if ($class) { + echo $class . $type . $function . '()'; + } else { + echo $function . '()'; + } + echo '
'; + } + echo '
'; + + echo ''; + } else { + // CLI环境,使用文本格式输出 + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $file = isset($trace[0]['file']) ? $trace[0]['file'] : '未知文件'; + $line = isset($trace[0]['line']) ? $trace[0]['line'] : '未知行号'; + + echo "\n\033[1;36m调试输出 - {$file} (第 {$line} 行)\033[0m\n\n"; + + foreach ($args as $index => $arg) { + echo "\033[1;33m变量 #" . ($index + 1) . "\033[0m\n"; + echo self::formatVarCli($arg) . "\n\n"; + } + + // 打印调用栈 + echo "\033[1;33m调用栈:\033[0m\n"; + $traces = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); + foreach ($traces as $i => $t) { + if ($i === 0) continue; // 跳过当前函数 + $class = isset($t['class']) ? $t['class'] : ''; + $type = isset($t['type']) ? $t['type'] : ''; + $function = isset($t['function']) ? $t['function'] : ''; + $file = isset($t['file']) ? $t['file'] : '未知文件'; + $line = isset($t['line']) ? $t['line'] : '未知行号'; + + echo "#" . $i . " " . $file . " (" . $line . "): "; + if ($class) { + echo $class . $type . $function . "()\n"; + } else { + echo $function . "()\n"; + } + } + } + + // 终止程序 + exit(1); + } + + /** + * 美化打印变量但不终止程序 + * 可以传入任意数量的参数 + * + * @return void + */ + public static function dump() + { + $args = func_get_args(); + + // 复用dd的逻辑,但不终止程序 + ob_start(); + call_user_func_array([self::class, 'dd'], $args); + $output = ob_get_clean(); + + echo $output; + + // 不终止程序继续执行 + return; + } + + /** + * 格式化变量输出(HTML) + * + * @param mixed $var 需要格式化的变量 + * @param int $depth 当前递归深度 + * @param int $maxDepth 最大递归深度 + * @return string + */ + private static function formatVar($var, $depth = 0, $maxDepth = 10) + { + // 防止递归过深 + if ($depth > $maxDepth) { + return '** 最大递归深度 **'; + } + + $output = ''; + + // 根据变量类型格式化输出 + switch (gettype($var)) { + case 'NULL': + $output .= 'null'; + break; + + case 'boolean': + $output .= '' . ($var ? 'true' : 'false') . ''; + break; + + case 'integer': + case 'double': + $output .= '' . $var . ''; + break; + + case 'string': + $output .= 'string(' . strlen($var) . ') "' . htmlspecialchars($var) . '"'; + break; + + case 'array': + $count = count($var); + $output .= 'array(' . $count . ') {
'; + $indent = str_repeat('    ', $depth + 1); + + foreach ($var as $key => $value) { + $output .= $indent . '[' . htmlspecialchars($key) . '] => '; + $output .= self::formatVar($value, $depth + 1, $maxDepth) . '
'; + } + + if ($count > 0) { + $output .= str_repeat('    ', $depth); + } + $output .= '}'; + break; + + case 'object': + $id = spl_object_id($var); + $class = get_class($var); + $output .= 'object(' . $class . '#' . $id . ') {
'; + + $indent = str_repeat('    ', $depth + 1); + + // 获取对象属性 + $reflection = new \ReflectionObject($var); + $properties = $reflection->getProperties(); + + foreach ($properties as $property) { + $property->setAccessible(true); + $propName = $property->getName(); + + $visibility = ''; + if ($property->isPublic()) { + $visibility = 'public'; + } elseif ($property->isProtected()) { + $visibility = 'protected'; + $propName = '*' . $propName; + } elseif ($property->isPrivate()) { + $visibility = 'private'; + $propName = '#' . $propName; + } + + $output .= $indent . '[' . $visibility . ' ' . $propName . '] => '; + + if ($property->isInitialized($var)) { + $propValue = $property->getValue($var); + $output .= self::formatVar($propValue, $depth + 1, $maxDepth) . '
'; + } else { + $output .= 'uninitialized
'; + } + } + + if (count($properties) > 0) { + $output .= str_repeat('    ', $depth); + } + $output .= '}'; + break; + + case 'resource': + $output .= 'resource(' . get_resource_type($var) . ')'; + break; + + default: + $output .= '' . gettype($var) . ': ' . htmlspecialchars((string)$var); + break; + } + + return $output; + } + + /** + * 格式化变量输出(CLI) + * + * @param mixed $var 需要格式化的变量 + * @param int $depth 当前递归深度 + * @param int $maxDepth 最大递归深度 + * @return string + */ + private static function formatVarCli($var, $depth = 0, $maxDepth = 10) + { + // 防止递归过深 + if ($depth > $maxDepth) { + return "\033[1;30m** 最大递归深度 **\033[0m"; + } + + $output = ''; + $indent = str_repeat(' ', $depth); + + // 根据变量类型格式化输出 + switch (gettype($var)) { + case 'NULL': + $output .= "\033[1;30mnull\033[0m"; + break; + + case 'boolean': + $output .= "\033[0;36m" . ($var ? 'true' : 'false') . "\033[0m"; + break; + + case 'integer': + case 'double': + $output .= "\033[0;36m" . $var . "\033[0m"; + break; + + case 'string': + $output .= "\033[0;32mstring(" . strlen($var) . ")\033[0m \"" . $var . "\""; + break; + + case 'array': + $count = count($var); + $output .= "\033[0;32marray(" . $count . ")\033[0m {"; + + if ($count > 0) { + $output .= "\n"; + + foreach ($var as $key => $value) { + $output .= $indent . ' [' . "\033[0;35m" . $key . "\033[0m" . '] => '; + $output .= self::formatVarCli($value, $depth + 1, $maxDepth) . "\n"; + } + + $output .= $indent; + } + + $output .= '}'; + break; + + case 'object': + $id = spl_object_id($var); + $class = get_class($var); + $output .= "\033[0;32mobject(" . $class . "#" . $id . ")\033[0m {"; + + // 获取对象属性 + $reflection = new \ReflectionObject($var); + $properties = $reflection->getProperties(); + + if (count($properties) > 0) { + $output .= "\n"; + + foreach ($properties as $property) { + $property->setAccessible(true); + $propName = $property->getName(); + + $visibility = ''; + if ($property->isPublic()) { + $visibility = 'public'; + } elseif ($property->isProtected()) { + $visibility = 'protected'; + $propName = '*' . $propName; + } elseif ($property->isPrivate()) { + $visibility = 'private'; + $propName = '#' . $propName; + } + + $output .= $indent . ' [' . "\033[0;35m" . $visibility . ' ' . $propName . "\033[0m" . '] => '; + + if ($property->isInitialized($var)) { + $propValue = $property->getValue($var); + $output .= self::formatVarCli($propValue, $depth + 1, $maxDepth) . "\n"; + } else { + $output .= "\033[1;30muninitialized\033[0m\n"; + } + } + + $output .= $indent; + } + + $output .= '}'; + break; + + case 'resource': + $output .= "\033[0;32mresource(" . get_resource_type($var) . ")\033[0m"; + break; + + default: + $output .= "\033[0;32m" . gettype($var) . "\033[0m: " . (string)$var; + break; + } + + return $output; + } +} \ No newline at end of file