Skip to content

调试输出函数

概述

PHP 提供了多个内置函数用于调试和输出变量信息。var_dumpprint_rdebug_zval_dump 是最常用的三个调试函数,它们各有特点,适用于不同的调试场景。合理使用这些函数能显著提升开发效率。

PHP 版本说明

  • var_dump / print_r:PHP 所有版本可用
  • debug_zval_dump:PHP 所有版本可用,显示引用计数
  • var_export:可返回合法 PHP 代码表示
  • xdebug 扩展:增强 var_dump 输出,添加 HTML 格式和颜色

基础概念

调试函数对比

函数输出格式返回值适用场景
var_dump()类型 + 值 + 长度void查看变量类型和结构
print_r()人类可读格式void / string快速查看数组/对象结构
debug_zval_dump()类型 + 值 + 引用计数void调试引用/内存问题
var_export()合法 PHP 代码void / string导出变量为 PHP 代码

xdebug 对 var_dump 的增强

安装 xdebug 扩展后,var_dump 输出会自动变为 HTML 格式并添加颜色高亮,大幅提升可读性。

语法与代码

var_dump — 类型与值详情

php
<?php

declare(strict_types=1);

$integer = 42;
$string  = 'hello';
$float   = 3.14;
$boolean = true;
$null    = null;
$array   = ['name' => 'Alice', 'age' => 30, 'scores' => [90, 85, 92]];

var_dump($integer);
// int(42)

var_dump($string);
// string(5) "hello"

var_dump($float);
// float(3.14)

var_dump($boolean);
// bool(true)

var_dump($null);
// NULL

var_dump($array);
// array(3) {
//   ["name"]=>
//   string(5) "Alice"
//   ["age"]=>
//   int(30)
//   ["scores"]=>
//   array(3) {
//     [0]=>
//     int(90)
//     [1]=>
//     int(85)
//     [2]=>
//     int(92)
//   }
// }
php
<?php

declare(strict_types=1);

$user = [
    'name'  => 'Alice',
    'age'   => 30,
    'roles' => ['admin', 'editor'],
    'active' => true,
    null,
];

// 直接输出
print_r($user);
// Array
// (
//     [name] => Alice
//     [age] => 30
//     [roles] => Array
//         (
//             [0] => admin
//             [1] => editor
//         )
//     [active] => 1
//     [0] =>
// )

// 第二个参数为 true 时返回字符串而非直接输出
$output = print_r($user, true);
error_log($output); // 写入日志

debug_zval_dump — 引用计数调试

php
<?php

declare(strict_types=1);

$a = 'hello';
$b = $a;     // 引用复制
$c = &$a;    // 引用赋值

debug_zval_dump($a);
// string(5) "hello" refcount(3)  ← 3 个引用:$a, $b, &$c

debug_zval_dump($b);
// string(5) "hello" refcount(2)  ← 2 个引用(独立副本)

unset($c);
debug_zval_dump($a);
// string(5) "hello" refcount(2)  ← 引用计数减少

注意

debug_zval_dump 显示的 refcount 比实际值多 1,因为函数调用本身也算一个引用。

var_export — 导出为 PHP 代码

php
<?php

declare(strict_types=1);

$config = [
    'database' => [
        'host' => 'localhost',
        'port' => 3306,
        'name' => 'myapp',
    ],
    'debug' => false,
    'version' => '1.0.0',
];

// 直接输出
var_export($config);
// array (
//   'database' =>
//   array (
//     'host' => 'localhost',
//     'port' => 3306,
//     'name' => 'myapp',
//   ),
//   'debug' => false,
//   'version' => '1.0.0',
// )

// 返回字符串
$exported = var_export($config, true);

// 写入配置缓存文件
file_put_contents('/tmp/config.cache.php', "<?php\nreturn {$exported};");

详细说明

var_dump 的深度限制

var_dump 没有内置深度限制,对于深层嵌套的数据结构可能导致内存溢出。自定义限制的方式:

php
<?php

declare(strict_types=1);

function safeVarDump(mixed $var, int $maxDepth = 3, int $currentDepth = 0): void
{
    if ($currentDepth >= $maxDepth) {
        echo str_repeat('  ', $currentDepth) . "*MAX DEPTH REACHED*\n";
        return;
    }

    $indent = str_repeat('  ', $currentDepth);

    if (is_array($var)) {
        echo $indent . "array(" . count($var) . ") {\n";
        foreach ($var as $key => $value) {
            echo $indent . "  [{$key}] =>\n";
            safeVarDump($value, $maxDepth, $currentDepth + 2);
        }
        echo $indent . "}\n";
    } elseif (is_object($var)) {
        echo $indent . get_class($var) . " Object\n";
    } else {
        var_dump($var);
    }
}

xdebug 配置优化

xdebug 扩展可以大幅增强 var_dump 的输出效果。以下是 php.ini 中的推荐配置:

ini
; xdebug 基本配置
xdebug.mode = debug,develop

; var_dump 增强(HTML 模式,CLI 环境无效)
xdebug.var_display_max_children = 256
xdebug.var_display_max_data = 1024
xdebug.var_display_max_depth = 4

; 覆盖 var_dump
; CLI 环境下自动使用文本格式

VSCode 配置 XDebug

在项目根目录创建 .vscode/launch.json

json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "log": true,
            "pathMappings": {
                "/app": "${workspaceFolder}"
            }
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9003
        }
    ]
}

PHP 配置(php.inixdebug.ini):

ini
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.client_host = localhost
xdebug.client_port = 9003
xdebug.discover_client_host = true
xdebug.idekey = VSCODE

XDebug 版本对照

  • PHP 8.0~8.2 → XDebug 3.x
  • PHP 8.3+ → XDebug 3.3+
  • PHP 8.4+ → XDebug 3.4+

实战示例

开发环境调试辅助类

php
<?php

declare(strict_types=1);

class Dumper
{
    private static bool $enabled = true;

    public static function disable(): void
    {
        self::$enabled = false;
    }

    /**
     * 格式化输出变量信息到 stderr
     */
    public static function dump(mixed ...$vars): void
    {
        if (!self::$enabled) {
            return;
        }

        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
        $file = basename($trace['file']);
        $line = $trace['line'];

        $output = "--- DUMP @ {$file}:{$line} ---\n";
        foreach ($vars as $i => $var) {
            $label = count($vars) > 1 ? "#" . ($i + 1) . " " : "";
            $output .= $label . self::formatVar($var) . "\n";
        }
        $output .= str_repeat('-', 40) . "\n";

        if (php_sapi_name() === 'cli') {
            fwrite(STDERR, $output);
        } else {
            // Web 环境使用 HTML 格式
            echo '<pre style="background:#1e1e1e;color:#d4d4d4;padding:12px;margin:8px 0;'
                . 'border-radius:4px;overflow-x:auto;font-size:13px;">'
                . htmlspecialchars($output)
                . '</pre>';
        }
    }

    /**
     * 导出变量到日志文件
     */
    public static function dumpToFile(mixed $var, string $file = '/tmp/debug.log'): void
    {
        $content = sprintf(
            "[%s] %s:%d\n%s\n\n",
            date('Y-m-d H:i:s'),
            basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file']),
            debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['line'],
            self::formatVar($var)
        );
        file_put_contents($file, $content, FILE_APPEND);
    }

    private static function formatVar(mixed $var): string
    {
        ob_start();
        var_dump($var);
        return trim(ob_get_clean());
    }
}

// 使用示例
Dumper::dump('hello', [1, 2, 3], 42);
Dumper::dumpToFile(['user' => 'Alice', 'id' => 1]);

注意事项

  1. 生产环境禁用调试输出var_dump 等函数不应在生产环境代码中遗留,可能泄露敏感信息。

  2. var_dump 对大数组的性能问题:对于包含数千元素的数组,var_dump 可能导致内存溢出。使用 print_r($arr, true) 并限制输出长度。

  3. print_r 会输出到缓冲区print_r() 默认直接输出。如果需要在日志中使用,第二个参数传 true 获取字符串返回值。

  4. debug_zval_dump 的 refcount 不完全准确:引用计数受函数调用栈影响,debug_zval_dump 显示的值通常比实际多 1。

  5. var_export 不支持资源类型:尝试 var_export 导出资源类型(如文件句柄、数据库连接)会产生警告。

  6. xdebug 增强 var_dump 仅影响 HTML 输出:CLI 模式下 xdebug 对 var_dump 的影响有限,但仍会限制输出深度和长度。

最佳实践

推荐做法

  1. 开发环境用 xdebug 增强 var_dump:安装 xdebug 扩展,获得彩色 HTML 格式输出
  2. 封装统一的调试函数:添加调用位置(文件:行号)、支持多变量同时输出
  3. 使用条件编译:通过常量 APP_DEBUG 控制调试函数是否生效
  4. 日志中使用 print_r($var, true):获取字符串后写入日志,而非直接输出
  5. 调试完毕及时删除:提交代码前,搜索并移除所有调试输出

进阶用法

调试与测试技巧

php
<?php
declare(strict_types=1);

// 单元测试辅助函数
function createTestResource(): mixed
{
    return match (true) {
        default => new stdClass(),
    };
}

// 调试输出函数
function debugOutput(mixed , string  = ''): void
{
     =  ? ": " : '';
     .= print_r(, true);
    fwrite(STDERR,  . "\n");
}

// 性能基准测试
function benchmark(callable , int  = 1000): float
{
     = hrtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $fn();
    }
    return (hrtime(true) - $start) / 1e9;
}

日志记录实践

php
<?php
declare(strict_types=1);

/**
 * 简易日志记录器
 */
class SimpleLogger
{
    private string $logFile;
    private string $level = 'INFO';

    public function __construct(string $logFile)
    {
        $this->logFile = $logFile;
    }

    public function info(string $message, array $context = []): void
    {
        $this->log('INFO', $message, $context);
    }

    public function warning(string $message, array $context = []): void
    {
        $this->log('WARNING', $message, $context);
    }

    public function error(string $message, array $context = []): void
    {
        $this->log('ERROR', $message, $context);
    }

    private function log(string $level, string $message, array $context): void
    {
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
        $line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
    }
}

配置与环境检测

php
<?php
declare(strict_types=1);

// 环境检测工具
class EnvironmentChecker
{
    public static function checkRequirements(array $requirements): array
    {
        $results = [];
        foreach ($requirements as $name => $check) {
            $results[$name] = is_callable($check) ? $check() : false;
        }
        return $results;
    }

    public static function getSystemInfo(): array
    {
        return [
            'php_version' => PHP_VERSION,
            'os' => PHP_OS,
            'sapi' => PHP_SAPI,
            'memory_limit' => ini_get('memory_limit'),
            'max_execution_time' => ini_get('max_execution_time'),
            'loaded_extensions' => get_loaded_extensions(),
        ];
    }
}

常见问题排查

问题可能原因解决方案
连接超时网络问题/配置错误检查配置,增加超时时间
权限不足文件/目录权限使用 chmod/chown 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 8.0__construct(public $x)
php
<?php
declare(strict_types=1);

// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
    if (version_compare(PHP_VERSION, $minVersion, '<')) {
        throw new RuntimeException(
            sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
        );
    }
}

ensureVersion('8.1.0');

参考链接