Skip to content

PHP 错误处理

有效的错误处理对于构建健壮且安全的 PHP 应用程序至关重要。它包括检测错误、适当地报告它们(为开发者记录日志,为用户显示友好消息)以及在可能的情况下从中恢复。

PHP 区分几种不同类型的问题:

  • 通知(Notices): 脚本执行期间发现的非关键问题(例如,使用了未定义的变量)。脚本通常会继续执行。
  • 警告(Warnings): 更严重的非致命错误(例如,包含了不存在的文件)。脚本通常会继续执行。
  • 致命错误(Fatal Errors): 中止脚本执行的严重错误(例如,调用了未定义的函数、解析错误)。在 PHP 7+ 中,许多此类错误现在会抛出 Error 异常。
  • 异常(Exceptions): 一种面向对象的方式,用于处理运行时错误或异常情况(实现了 Throwable 接口的 Exception 和 Error 类)。将在异常章节中介绍。

现代 PHP 严重依赖于异常模型(try...catch...finally)来处理运行时问题,包括许多以前属于致命错误的错误。

基本错误处理:die() 或 exit() 结构

Section titled “基本错误处理:die() 或 exit() 结构”

处理致命情况的一种非常简单但通常比较粗糙的方法是使用 die() 或 exit()(它们是别名)来停止脚本执行。

示例:在打开文件前检查文件是否存在:

<?php
$filename = "nonexistent_file.txt";
if (!file_exists($filename)) {
// Stop script execution and output a message
die("Error: File '{$filename}' not found. Cannot proceed.");
}
// This code will only run if the file exists
echo "File exists, proceeding...";
$fileHandle = fopen($filename, "r");
// ... process file ...
?>

尽管 die() 可以阻止在发生严重错误后进一步执行,但它提供的用户体验很差,并且绕过了任何清理逻辑(例如关闭数据库连接)。在生产应用程序中,通常不建议使用它,而应优先考虑异常或更优雅的错误处理方式。

PHP 的错误报告行为由 php.ini 配置文件中的设置控制,或者在脚本中通过 error_reporting() 和 ini_set() 等函数动态控制。

关键指令(php.ini):

  • error_reporting(例如 E_ALL):设置 PHP 应该报告的错误级别。开发期间建议使用 E_ALL 来捕获所有问题(通知、警告等)。
  • display_errors(例如 On 或 Off):确定是否应在浏览器输出中直接显示错误。开发期间应设置为 On,但在生产环境中必须设置为 Off,以避免暴露敏感信息。
  • log_errors(例如 On 或 Off):确定是否应将错误写入日志文件。生产环境应设置为 On。
  • error_log(例如 /var/log/php_errors.log 或 syslog):指定当 log_errors 设置为 On 时,错误应记录到哪个文件或系统日志(syslog)。

开发环境与生产环境设置:

  • 开发环境: error_reporting = E_ALL,display_errors = On,log_errors = On(可选,但推荐)。
  • 生产环境: error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT(或类似设置,如果需要,报告除通知/废弃警告之外的大多数错误),display_errors = Off,log_errors = On,error_log = /path/to/secure/log/file。

你可以在脚本中临时覆盖 php.ini 设置:

<?php
// Recommended for development: Show all errors
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
// Example of triggering a notice
echo $undefinedVariable;
// Code that might cause a warning
include 'non_existent_file.php';
?>

自定义错误处理程序 (set_error_handler)

Section titled “自定义错误处理程序 (set_error_handler)”

对于非异常的错误(主要是通知和警告),你可以使用 set_error_handler() 定义一个自定义函数来处理它们。这允许你拦截这些错误并决定如何记录或显示它们。

鉴于现在许多致命错误会抛出 Error 异常,这种方法的需求已不如以前普遍,但它对于在开发期间记录通知/警告或将特定警告转换为异常仍然有用。

处理函数接收的参数包括错误级别、消息、文件、行号和上下文。

语法:set_error_handler(callable $callback, int $error_levels = E_ALL | E_STRICT)

示例:记录警告和通知:

<?php
declare(strict_types=1);
// Custom error handler function
function customErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool
{
// Log the error (adjust path and format as needed)
$logMessage = "[" . date("Y-m-d H:i:s") . "] Error [$errno]: $errstr in $errfile on line $errline\n";
error_log($logMessage, 3, "/path/to/your/app_errors.log");
// To prevent PHP's internal handler from running, return true.
// If you return false or nothing, PHP's standard error handling will proceed.
// Don't display errors directly in production!
if (ini_get('display_errors')) { // Only display if configured
echo "**Error Caught:** [$errno] $errstr in $errfile:$errline<br>";
}
// Let PHP continue if it's just a notice/warning, but halt for user errors maybe?
// This logic depends on your needs.
/*
switch ($errno) {
case E_USER_ERROR:
echo "Fatal error caught. Halting.";
exit(1);
break;
case E_USER_WARNING:
case E_USER_NOTICE:
// Logged above, continue execution
break;
default:
// Standard PHP notice/warning, logged above, continue execution
break;
}
*/
return true; // Suppress PHP's default handler for handled types
}
// Set the custom handler to catch all errors (including notices, warnings)
set_error_handler('customErrorHandler', E_ALL);
// Trigger a notice
echo $someUndefinedVar;
// Trigger a warning
include 'non_existent_script.php';
echo "Script execution might continue depending on handler logic...";
?>

注意:由 set_error_handler() 设置的自定义错误处理程序无法处理像解析错误这样的致命错误,或者 PHP 7+ 中现在会抛出 Error 异常的大多数错误。可以使用 register_shutdown_function() 来检测某些致命错误,或使用 try...catch (Throwable $t) 来处理异常/错误。

你可以使用 trigger_error() 函数在代码中生成用户级别的错误(通知、警告或致命错误)。这对于指示应用程序逻辑或库使用中的问题非常有用。

语法:trigger_error(string $message, int $error_type = E_USER_NOTICE)

错误类型:

  • E_USER_NOTICE(默认):类似于标准的 PHP 通知。
  • E_USER_WARNING:类似于标准的 PHP 警告。
  • E_USER_ERROR:类似于致命的 PHP 错误(会中止执行,除非被自定义错误处理程序捕获并退出)。
  • E_USER_DEPRECATED:指示已弃用的功能。

示例:

<?php
function calculateDivision(float $numerator, float $denominator): float {
if ($denominator == 0) {
// Trigger a user warning for invalid input
trigger_error("Division by zero attempted", E_USER_WARNING);
return NAN; // Not a Number
}
if ($numerator < 0) {
// Example: Trigger a notice if numerator is negative
trigger_error("Numerator is negative, proceeding anyway.", E_USER_NOTICE);
}
return $numerator / $denominator;
}
// Set a simple error handler to see the triggered errors
set_error_handler(function($errno, $errstr){
echo "Triggered Error: $errstr\n";
}, E_USER_NOTICE | E_USER_WARNING);
$result1 = calculateDivision(10, 0);
echo "Result 1: $result1\n";
$result2 = calculateDivision(-5, 2);
echo "Result 2: $result2\n";
?>

输出:

Triggered Error: Division by zero attempted
Result 1: NAN
Triggered Error: Numerator is negative, proceeding anyway.
Result 2: -2.5

error_log() 函数允许你显式地将消息发送到 PHP 配置的错误日志机制(文件、syslog),甚至通过电子邮件发送(尽管对于大量日志,通常不建议使用电子邮件日志)。

这对于记录特定的应用程序事件或详细的调试信息非常有用,可以将它们与标准的 PHP 错误分开。

语法:error_log(string $message, int $message_type = 0, ?string $destination = null, ?string $additional_headers = null): bool

常见的 message_type 值:

  • 0(默认):将消息发送到 PHP 的系统日志记录器(使用 php.ini 中的 error_log 指令)。
  • 1:通过电子邮件将消息发送到 destination 参数指定的地址。
  • 3:将消息附加到 destination 指定的文件。

示例:将自定义消息记录到标准的 PHP 错误日志:

<?php
$userId = 123;
$action = "update_profile";
// ... some logic ...
if (/* some condition indicating success */ true) {
$logMsg = "User ID {$userId} successfully performed action '{$action}'.";
error_log($logMsg); // Sends to the configured PHP error log (type 0)
echo "Action logged.";
} else {
$logMsg = "User ID {$userId} failed action '{$action}'.";
error_log($logMsg); // Log failure too
echo "Action failure logged.";
}
?>

示例:记录到特定文件(确保权限正确):

<?php
$customLogFile = '/var/log/my_app_debug.log'; // Make sure this file is writable by the web server
$debugInfo = "Processing started for request ID: " . uniqid();
error_log(date('[Y-m-d H:i:s] ') . $debugInfo . "\n", 3, $customLogFile);
echo "Debug info written to custom log.";
?>

注意:避免记录敏感数据,例如密码或个人信息。