Skip to content

var_dump / print_r / var_export

概述

PHP 提供了多种用于输出和检查变量内容的调试函数。var_dump()print_r()var_export() 是最常用的三个,它们各有特色:var_dump() 显示详细的类型和值信息,print_r() 提供人类可读的输出,var_export() 生成可执行的 PHP 代码。此外还有 debug_zval_dump() 可以查看引用计数信息。

PHP 版本说明

  • var_dump()print_r()var_export() 自 PHP 4 起可用
  • debug_zval_dump() 自 PHP 5 起可用
  • var_dump 在 PHP 8.0 中对 falsenull 的显示有改进
  • var_export 在 PHP 8.3 中支持 __set_state 的更多类型

基础概念

调试输出函数对比

函数用途输出格式是否有返回值主要场景
var_dump详细调试类型+值开发调试
print_r可读输出简洁格式简单查看
var_exportPHP 代码可执行代码缓存/序列化
debug_zval_dump引用调试引用计数内存分析

语法与代码

var_dump()

var_dump() 输出一个或多个表达式的类型和值,是最常用的调试函数。

php
<?php

declare(strict_types=1);

// 基本类型
var_dump(42);
// int(42)

var_dump(3.14);
// float(3.14)

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

var_dump(true);
// bool(true)

var_dump(null);
// NULL

var_dump([1, 'two', 3.0]);
// array(3) {
//   [0]=> int(1)
//   [1]=> string(3) "two"
//   [2]=> float(3)
// }

// 多个变量同时输出
var_dump('name', 42, true, null);
// string(4) "name" int(42) bool(true) NULL

// 对象输出
class User
{
    public function __construct(
        public string $name = 'Alice',
        public int $age = 30,
        private string $email = 'alice@example.com'
    ) {}
}

var_dump(new User());
// object(User)#1 (3) { ["name"]=> string(5) "Alice" ["age"]=> int(30) ["email":"User":private]=> string(17) "alice@example.com" }

print_r() 以人类可读的格式输出变量的值。

php
<?php

declare(strict_types=1);

// 数组输出
$user = [
    'name' => 'Alice',
    'age' => 30,
    'skills' => ['PHP', 'JavaScript', 'MySQL'],
];

print_r($user);
// Array
// (
//     [name] => Alice
//     [age] => 30
//     [skills] => Array
//         (
//             [0] => PHP
//             [1] => JavaScript
//             [2] => MySQL
//         )
// )

// 使用第二个参数返回而非输出
$output = print_r($user, true);
echo "Output length: " . strlen($output) . "\n";

// 嵌套数组和对象
$data = [
    'users' => [
        ['id' => 1, 'name' => 'Alice'],
        ['id' => 2, 'name' => 'Bob'],
    ],
    'count' => 2,
];

print_r($data);

var_export()

var_export() 输出或返回变量的可执行 PHP 代码表示。

php
<?php

declare(strict_types=1);

// 数组导出
$config = [
    'debug' => true,
    'cache' => false,
    'timeout' => 30,
    'allowedOrigins' => ['*'],
];

var_export($config);
// array (
//   'debug' => true,
//   'cache' => false,
//   'timeout' => 30,
//   'allowedOrigins' =>
//   array (
//     0 => '*',
//   ),
// )

// 返回字符串(不直接输出)
$code = var_export($config, true);
echo $code . "\n";

// 用导出的代码重建数组
$evaluated = eval('return ' . $code . ';');
var_dump($evaluated === $config); // true

// 写入文件作为配置缓存
$configFile = '/tmp/config_cache.php';
$configContent = '<?php return ' . var_export($config, true) . ';';
file_put_contents($configFile, $configContent);

// 读取配置缓存(比 JSON/INI 更快)
$cachedConfig = require $configFile;
var_dump($cachedConfig);

debug_zval_dump()

debug_zval_dump() 显示变量的引用计数(refcount),用于调试内存和引用问题。

php
<?php

declare(strict_types=1);

$a = 'hello';
debug_zval_dump($a);
// string(5) "hello" refcount(2) — refcount 包含函数调用本身的引用

$b = $a; // 值复制
debug_zval_dump($a, $b);
// string(5) "hello" refcount(3) — $a, $b, 函数调用

$c = &$a; // 引用
debug_zval_dump($a, $c);
// string(5) "hello" refcount(2) — $a 和 $c 共享同一个引用

// 数组引用计数
$arr = [1, 2, 3];
debug_zval_dump($arr);
// array(3) { [0]=> long(1) refcount(1) [1]=> long(2) refcount(1) [2]=> long(3) refcount(1) }

引用计数准确性

debug_zval_dump() 的 refcount 包含函数调用本身的引用(+1)。在现代 PHP(7.0+)中,引用计数机制已更加复杂,debug_zval_dump() 的输出仅供参考。

格式化输出

php
<?php

declare(strict_types=1);

// 自定义格式化输出函数
function dump(mixed ...$vars): void
{
    echo "=== Debug Output ===\n";
    foreach ($vars as $i => $var) {
        $label = "#{$i}";
        echo "[{$label}] " . get_debug_type($var) . ":\n";

        if (is_array($var) || is_object($var)) {
            echo print_r($var, true) . "\n";
        } else {
            var_dump($var);
        }
    }
    echo "==================\n";
}

dump(42, 'hello', [1, 2, 3], true, null);

// JSON 格式化输出(适合 API 调试)
function dumpJson(mixed $data): void
{
    echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
}

$apiResponse = [
    'status' => 'success',
    'data' => ['id' => 1, 'name' => 'Alice'],
    'timestamp' => time(),
];

dumpJson($apiResponse);

详细说明

php
<?php

declare(strict_types=1);

// print_r 不显示类型信息
$value = '42';
print_r($value);   // 42(看不出是字符串还是整数)

// var_dump 显示完整类型信息
var_dump($value);   // string(2) "42"

// print_r 第二个参数可以返回字符串
$array = ['a' => 1, 'b' => 2];
$str = print_r($array, true);
echo "Length: " . strlen($str) . "\n";

// var_dump 无返回值,只能直接输出
// $str = var_dump($array); // void

// 布尔值和 null 的输出差异
var_dump(false);    // bool(false)
print_r(false);     // (无输出,或输出空)
var_dump(null);     // NULL
print_r(null);      // (无输出,或输出空)

var_export 的限制

php
<?php

declare(strict_types=1);

// var_export 可以处理标量和数组
var_export(42);
var_export('hello');
var_export([1, 2, 3]);

// 对象需要支持 __set_state 魔术方法
class Config
{
    public function __construct(public readonly array $items = []) {}

    public static function __set_state(array $array): self
    {
        return new self($array['items']);
    }
}

$config = new Config(['debug' => true, 'cache' => false]);
var_export($config);
// \Config::__set_state(array('items' => array('debug' => true, 'cache' => false)))

// 不支持的资源类型
// $fp = fopen('test.txt', 'r');
// var_export($fp); // 导出不完整的资源表示

实战示例

配置缓存系统

php
<?php

declare(strict_types=1);

class ConfigCache
{
    public function __construct(private readonly string $cacheDir) {}

    public function write(string $name, array $config): void
    {
        $content = '<?php return ' . var_export($config, true) . ';';
        $path = $this->cacheDir . "/{$name}.php";

        file_put_contents($path, $content);
    }

    public function read(string $name): array
    {
        $path = $this->cacheDir . "/{$name}.php";

        if (!file_exists($path)) {
            return [];
        }

        return require $path;
    }

    public function isFresh(string $name, int $ttl = 3600): bool
    {
        $path = $this->cacheDir . "/{$name}.php";

        return file_exists($path) && (time() - filemtime($path)) < $ttl;
    }
}

$cache = new ConfigCache('/tmp/config');

$appConfig = [
    'app' => [
        'name' => 'MyApp',
        'env' => 'production',
        'debug' => false,
    ],
    'database' => [
        'host' => 'localhost',
        'port' => 3306,
        'name' => 'myapp_production',
    ],
];

$cache->write('app', $appConfig);
$loaded = $cache->read('app');

echo is_array($loaded) && $loaded === $appConfig ? 'Cache matched' : 'Cache mismatch';
// Cache matched

调试辅助工具

php
<?php

declare(strict_types=1);

// 开发环境下的安全 dump 函数
function dd(mixed ...$vars): never
{
    foreach ($vars as $var) {
        var_dump($var);
    }

    die(1);
}

// 带行号信息的 dump
function dumpWithLine(mixed $var, int $line = 0, string $file = ''): void
{
    $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
    $line = $line ?: ($trace[0]['line'] ?? 0);
    $file = $file ?: ($trace[0]['file'] ?? 'unknown');

    echo "Dump at {$file}:{$line}\n";
    var_dump($var);
}

// 条件 dump
function dumpIf(bool $condition, mixed $var): void
{
    if ($condition) {
        var_dump($var);
    }
}

$users = ['Alice' => 30, 'Bob' => 25];
dumpWithLine($users);
// Dump at /path/to/script.php:42
// array(2) { ["Alice"]=> int(30) ["Bob"]=> int(25) }

注意事项

常见陷阱

  1. var_dump 输出到 stdout
php
<?php

declare(strict_types=1);

// var_dump 总是输出到标准输出
// 如果需要在 Web 应用中收集输出,需要使用 ob_start()
ob_start();
var_dump(['key' => 'value']);
$dumpOutput = ob_get_clean();

// 或者使用 print_r 的第二个参数
$printROutput = print_r(['key' => 'value'], true);
  1. 大数组/对象的输出
php
<?php

declare(strict_types=1);

// 大数组输出可能导致浏览器卡顿
$largeArray = range(1, 10000);

// 限制输出
function safeDump(mixed $var, int $maxDepth = 3): void
{
    if (is_array($var)) {
        echo 'array(' . count($var) . ') { ... }' . "\n";
        $count = 0;
        foreach ($var as $key => $value) {
            if ($count >= 10) {
                echo "  ... (truncated " . (count($var) - 10) . " items)\n";
                break;
            }
            echo "  [{$key}] => ";
            if (is_array($value) && $maxDepth > 1) {
                echo 'array(' . count($value) . ') { ... }';
            } else {
                var_dump($value);
            }
            $count++;
        }
    } else {
        var_dump($var);
    }
}

safeDump($largeArray);
  1. var_export 与 eval 的安全风险
php
<?php

declare(strict_types=1);

// var_export + eval 可能存在安全风险
// 只在可信环境下使用

// 推荐替代方案:使用 json_encode/json_decode
// 或使用 serialize/unserialize(注意安全问题)

最佳实践

1. 生产环境禁用调试输出

php
<?php

declare(strict_types=1);

// 使用环境判断
if (getenv('APP_DEBUG') === 'true') {
    function dd(mixed ...$vars): never
    {
        foreach ($vars as $var) {
            var_dump($var);
        }
        die(1);
    }
} else {
    function dd(mixed ...$vars): void
    {
        // 生产环境不输出
        error_log('dd called in production');
    }
}

2. 使用 var_export 缓存配置

php
<?php

declare(strict_types=1);

// 写入配置缓存
$config = ['debug' => false, 'cache' => true];
file_put_contents('/tmp/config.php', '<?php return ' . var_export($config, true) . ';');

// 读取配置缓存(比 parse_ini_file 和 json_decode 更快)
$config = require '/tmp/config.php';

3. 选择合适的调试函数

  • var_dump:需要类型和值的完整信息时
  • print_r:需要简洁的可读输出或返回字符串时
  • var_export:需要生成可执行的 PHP 代码时
  • dd:开发调试时快速终止并查看变量

参考链接