如何从生产 PHP 代码中获取正确的调试上下文? print_r vs var_export vs var_dump 极端情况
How to get proper debug context from production PHP code? print_r vs var_export vs var_dump corner cases
要求
我们在网络服务器上有生产 PHP 代码 运行。在某些情况下,我们希望 丰富调试输出 (例如 error.log) 使用上下文 信息。上下文可以有任意字段和结构。
我们的目标是找到一个通用的调试生成函数,它不会在任何极端情况下生成警告。如果只是部分输出是可以接受的。
TL;DR
所有三个 PHP 标准函数都会在某些上下文中引起警告:
- print_r & var_dump 无法处理关闭连接的 mysqli 对象
- var_export 无法处理递归
问题
有没有办法return将任意对象的上下文作为人类可读的字符串?
测试用例
// Dealing with recursion:
$recObj = new stdClass();
$recObj->recursion = $recObj;
print_r ($recObj); // works
var_export($recObj); // throws: "Warning: var_export does not handle circular references"
var_dump ($recObj); // works
// dealing with mysqli
$mysqli = mysqli_connect('mysql', 'root', 'myPass', 'mysql');
print_r ($mysqli); // works
var_export($mysqli); // works as in "does not throw warnings" but all values are null
var_dump ($mysqli); // works
// Try again with closed connection
mysqli_close($mysqli);
print_r ($mysqli); // throws: "Warning: print_r(): Couldn't fetch mysqli"
var_export($mysqli); // works (again all values are null)
var_dump ($mysqli); // throws: Warning: var_dump(): Couldn't fetch mysqli
根据关闭的 mysqli 连接的相关性:如果您在错误处理程序或已注册的关闭函数(这是一个很好的位置)中进行上下文打印,一旦您到达该处理程序,mysqli 对象将已经自动关闭连接。
如您所见,none 的内置输出方法给出了能够 return 任何上下文的预期结果。
考虑的选项
- 使用@notation 可以抑制警告。但是,已注册的错误处理程序和关闭函数仍然会因错误而被调用,并且需要自定义逻辑来忽略该特定错误。这可能会隐藏真正的错误,如果处理像 sentry.io
这样的第 3 方错误跟踪系统,也会变得很烦人
serialize()
函数在任何情况下都不会产生警告,但它缺乏人类可读性。
json_encode()
函数比 serialize 更易读,但它在递归测试用例中 return 没有任何意义....
@BlackXero 的评论是正确的,对我有用。
我没有找到内置打印功能,它在包含具有关闭连接的 mysqli 对象时不会导致错误/警告(我实际上将其归类为错误/不需要的行为)。
我们最终通过
添加了Symfony Vardumper
composer require symfony/var-dumper
并编写一个小辅助函数来显示来自 cli 脚本或浏览器的正确且漂亮的输出:
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
class Debug {
/**
* Method provides a function which can handle all our corner-cases for producing
* debug output.
*
* The corner cases are:
* - objects with recursion
* - mysqli references (also to closed connections in error handling)
*
* The returned result will be:
* - formatted for CLI if the script is run from cli
* - HTML formatted otherwise
* - The HTML formatted output is collapsed by default. Use CTRL-left click to
* expand/collapse all children
* - You can force html|cli formatting using the optional third parameter
*
* Uses the Symfony VarDumper composer module.
*
* @see https://github.com/symfony/var-dumper
* @see
* @param mixed $val - variable to be dumped
* @param bool $return - if true, will return the result as string
* @param string|null $format null|cli|html for forcing output format
* @return bool|string
*/
public static function varDump($val, $return = false, $format = null) {
if (is_null($format)) {
$format = php_sapi_name() == 'cli' ? 'cli' : 'html';
}
$cloner = new VarCloner();
if ($format === 'cli') {
$dumper = new CliDumper();
} else {
$dumper = new HtmlDumper();
}
$output = fopen('php://memory', 'r+b');
$dumper->dump($cloner->cloneVar($val), $output);
$res = stream_get_contents($output, -1, 0);
if ($return) {
return $res;
} else {
echo $res;
return true;
}
}
}
那个方法
- 可以处理我传递给它的所有输入而不会出现错误或警告
- 格式适合 CLI 和 HTML
- return 将结果作为字符串转发给外部错误跟踪系统,如 sentry
所以它符合我在最初问题中要求的所有方框。
感谢@BlackXero 正确理解问题并为我指明了正确的方向。
要求
我们在网络服务器上有生产 PHP 代码 运行。在某些情况下,我们希望 丰富调试输出 (例如 error.log) 使用上下文 信息。上下文可以有任意字段和结构。
我们的目标是找到一个通用的调试生成函数,它不会在任何极端情况下生成警告。如果只是部分输出是可以接受的。
TL;DR
所有三个 PHP 标准函数都会在某些上下文中引起警告:
- print_r & var_dump 无法处理关闭连接的 mysqli 对象
- var_export 无法处理递归
问题
有没有办法return将任意对象的上下文作为人类可读的字符串?
测试用例
// Dealing with recursion:
$recObj = new stdClass();
$recObj->recursion = $recObj;
print_r ($recObj); // works
var_export($recObj); // throws: "Warning: var_export does not handle circular references"
var_dump ($recObj); // works
// dealing with mysqli
$mysqli = mysqli_connect('mysql', 'root', 'myPass', 'mysql');
print_r ($mysqli); // works
var_export($mysqli); // works as in "does not throw warnings" but all values are null
var_dump ($mysqli); // works
// Try again with closed connection
mysqli_close($mysqli);
print_r ($mysqli); // throws: "Warning: print_r(): Couldn't fetch mysqli"
var_export($mysqli); // works (again all values are null)
var_dump ($mysqli); // throws: Warning: var_dump(): Couldn't fetch mysqli
根据关闭的 mysqli 连接的相关性:如果您在错误处理程序或已注册的关闭函数(这是一个很好的位置)中进行上下文打印,一旦您到达该处理程序,mysqli 对象将已经自动关闭连接。
如您所见,none 的内置输出方法给出了能够 return 任何上下文的预期结果。
考虑的选项
- 使用@notation 可以抑制警告。但是,已注册的错误处理程序和关闭函数仍然会因错误而被调用,并且需要自定义逻辑来忽略该特定错误。这可能会隐藏真正的错误,如果处理像 sentry.io 这样的第 3 方错误跟踪系统,也会变得很烦人
serialize()
函数在任何情况下都不会产生警告,但它缺乏人类可读性。json_encode()
函数比 serialize 更易读,但它在递归测试用例中 return 没有任何意义....
@BlackXero 的评论是正确的,对我有用。
我没有找到内置打印功能,它在包含具有关闭连接的 mysqli 对象时不会导致错误/警告(我实际上将其归类为错误/不需要的行为)。
我们最终通过
添加了Symfony Vardumpercomposer require symfony/var-dumper
并编写一个小辅助函数来显示来自 cli 脚本或浏览器的正确且漂亮的输出:
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
class Debug {
/**
* Method provides a function which can handle all our corner-cases for producing
* debug output.
*
* The corner cases are:
* - objects with recursion
* - mysqli references (also to closed connections in error handling)
*
* The returned result will be:
* - formatted for CLI if the script is run from cli
* - HTML formatted otherwise
* - The HTML formatted output is collapsed by default. Use CTRL-left click to
* expand/collapse all children
* - You can force html|cli formatting using the optional third parameter
*
* Uses the Symfony VarDumper composer module.
*
* @see https://github.com/symfony/var-dumper
* @see
* @param mixed $val - variable to be dumped
* @param bool $return - if true, will return the result as string
* @param string|null $format null|cli|html for forcing output format
* @return bool|string
*/
public static function varDump($val, $return = false, $format = null) {
if (is_null($format)) {
$format = php_sapi_name() == 'cli' ? 'cli' : 'html';
}
$cloner = new VarCloner();
if ($format === 'cli') {
$dumper = new CliDumper();
} else {
$dumper = new HtmlDumper();
}
$output = fopen('php://memory', 'r+b');
$dumper->dump($cloner->cloneVar($val), $output);
$res = stream_get_contents($output, -1, 0);
if ($return) {
return $res;
} else {
echo $res;
return true;
}
}
}
那个方法
- 可以处理我传递给它的所有输入而不会出现错误或警告
- 格式适合 CLI 和 HTML
- return 将结果作为字符串转发给外部错误跟踪系统,如 sentry
所以它符合我在最初问题中要求的所有方框。
感谢@BlackXero 正确理解问题并为我指明了正确的方向。