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 中对false和null的显示有改进var_export在 PHP 8.3 中支持__set_state的更多类型
基础概念
调试输出函数对比
| 函数 | 用途 | 输出格式 | 是否有返回值 | 主要场景 |
|---|---|---|---|---|
var_dump | 详细调试 | 类型+值 | 无 | 开发调试 |
print_r | 可读输出 | 简洁格式 | 有 | 简单查看 |
var_export | PHP 代码 | 可执行代码 | 有 | 缓存/序列化 |
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()
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);详细说明
print_r 与 var_dump 的核心区别
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) }注意事项
常见陷阱
- 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);- 大数组/对象的输出
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);- 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:开发调试时快速终止并查看变量