本文目录导读:

register_shutdown_function 是 PHP 中一个非常有用的函数,用于注册一个在脚本执行完成后(无论是正常结束还是发生致命错误)才会被调用的回调函数。
核心作用
捕获致命错误
最常见的用途是捕获 PHP 致命错误,因为 try-catch 无法捕获 E_ERROR 级别的错误。
register_shutdown_function(function() {
$error = error_get_last();
if ($error !== NULL) {
// 发生致命错误时执行清理操作
error_log("Fatal error: " . $error['message']);
}
});
资源清理
确保在脚本结束时释放资源,即使发生了意外错误也能正常执行:
// 数据库连接清理
$db = new mysqli('localhost', 'user', 'pass', 'db');
register_shutdown_function(function() use ($db) {
if ($db->connect_error === null) {
$db->close(); // 确保连接始终被关闭
}
});
日志记录
记录脚本执行结束的状态:
register_shutdown_function(function() {
$error = error_get_last();
if ($error !== null) {
// 记录错误日志
file_put_contents('error.log',
date('Y-m-d H:i:s') . " - " .
json_encode($error) . "\n",
FILE_APPEND);
} else {
// 记录正常结束
file_put_contents('access.log',
date('Y-m-d H:i:s') . " - Ended successfully\n",
FILE_APPEND);
}
});
框架中的统一处理
在 MVC 框架中统一处理异常和错误:
// 在框架入口文件设置
register_shutdown_function(function() {
$error = error_get_last();
if ($error && in_array($error['type'], [
E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR
])) {
// 显示友好的错误页面
ob_clean(); // 清除之前的输出
http_response_code(500);
include 'views/errors/500.php';
}
});
执行顺序说明
echo "Start\n";
register_shutdown_function(function() {
echo "Shutdown 1\n";
});
register_shutdown_function(function() {
echo "Shutdown 2\n";
});
// 输出:
// Start
// Shutdown 2 ← 后注册的先执行(LIFO)
// Shutdown 1
与析构函数的区别
| 特性 | register_shutdown_function | 析构函数 (__destruct) |
|---|---|---|
| 执行时机 | 脚本结束前 | 对象销毁时 |
| 执行顺序 | 后注册先执行 | 按对象销毁顺序 |
| 捕获致命错误 | 能捕获 | 不能捕获 |
| 参数传递 | 可以传递参数 | 不能接收参数 |
实际项目示例
完整的错误处理系统
class ErrorHandler {
public function __construct() {
register_shutdown_function([$this, 'handleShutdown']);
}
public function handleShutdown() {
$error = error_get_last();
if ($error === null) {
return; // 正常结束
}
// 处理不同类型的错误
switch ($error['type']) {
case E_ERROR:
case E_PARSE:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
$this->handleFatalError($error);
break;
}
}
private function handleFatalError($error) {
// 1. 记录错误到日志
error_log(sprintf(
"Fatal Error: %s in %s on line %d",
$error['message'],
$error['file'],
$error['line']
));
// 2. 发送错误通知(如邮件)
// $this->sendErrorNotification($error);
// 3. 显示用户友好界面
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: text/html; charset=utf-8');
}
// 清空可能的部分输出
while (ob_get_level()) {
ob_end_clean();
}
echo "系统遇到意外错误,请稍后再试";
}
}
// 使用
$handler = new ErrorHandler();
性能监控
class PerformanceMonitor {
private $startTime;
public function __construct() {
$this->startTime = microtime(true);
register_shutdown_function([$this, 'logPerformance']);
}
public function logPerformance() {
$duration = microtime(true) - $this->startTime;
$memory = memory_get_peak_usage(true) / 1024 / 1024;
// 记录性能数据
error_log(sprintf(
"Page load: %.2f sec, Memory: %.2f MB",
$duration,
$memory
));
// 如果执行时间过长,发出警告
if ($duration > 2) {
error_log("Warning: Page load time exceeded 2 seconds");
}
}
}
// 在应用入口使用
$monitor = new PerformanceMonitor();
注意事项
-
HTTP 头已发送问题:在 shutdown 函数中如果还想发送 HTTP 头,需要先检查
headers_sent()状态 -
不能修改输出:如果使用了输出缓冲,可以在函数内操作缓冲区
-
多个函数顺序:多个注册的函数按 LIFO(后进先出)顺序执行
-
耗时操作:不要在 shutdown 函数中执行耗时操作,会影响用户体验
-
PHP 7+ 兼容:在 PHP 7 中,可以使用
Throwable接口捕获更多错误类型
最佳实践
// 推荐的通用设置
register_shutdown_function(function() {
$error = error_get_last();
if ($error && in_array($error['type'],
[E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])
) {
// 清理输出缓冲
while (ob_get_level()) {
ob_end_clean();
}
// 记录致命错误
error_log("FATAL: {$error['message']} in {$error['file']}:{$error['line']}");
// 返回错误响应
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['error' => 'Internal server error']);
}
}
});
register_shutdown_function 是构建健壮 PHP 应用的重要工具,特别是对错误处理和资源清理提供了最后的安全网。