PHP 错误与异常处理
良好的错误处理是健壮应用的基础,PHP 提供了完整的错误和异常处理机制。
错误级别
| 常量 | 说明 |
|---|---|
E_ERROR | 致命错误,脚本终止 |
E_WARNING | 警告,脚本继续 |
E_NOTICE | 通知,轻微问题 |
E_DEPRECATED | 废弃功能警告 |
E_ALL | 所有错误 |
// 开发环境:显示所有错误 error_reporting(E_ALL); ini_set('display_errors', 1);`// 生产环境:记录错误,不显示 error_reporting(E_ALL); ini_set('display_errors', 0); ini_set('log_errors', 1); ini_set('error_log', '/var/log/php_errors.log'); ?> ``
异常处理
php <?php function divide(float $a, float $b): float { if ($b === 0.0) { throw new InvalidArgumentException("除数不能为零"); } return $a / $b; }`try { echo divide(10, 2); // 5 echo divide(10, 0); // 抛出异常 } catch (InvalidArgumentException $e) { echo "参数错误:" . $e->getMessage(); } catch (Exception $e) { echo "未知错误:" . $e->getMessage(); } finally { echo "无论如何都会执行"; // 清理资源 } ?>
`自定义异常
php <?php class AppException extends RuntimeException { public function __construct( string $message, private readonly string $context = '', int $code = 0, ?\Throwable $previous = null ) { parent::__construct($message, $code, $previous); }`public function getContext(): string { return $this->context; } }
class DatabaseException extends AppException {} class ValidationException extends AppException { private array $errors = [];
public function setErrors(array $errors): static { $this->errors = $errors; return $this; }
public function getErrors(): array { return $this->errors; } }
// 使用 try { $ex = new ValidationException("表单验证失败"); $ex->setErrors(["用户名不能为空", "邮箱格式错误"]); throw $ex; } catch (ValidationException $e) { foreach ($e->getErrors() as $err) { echo "- $err\n"; } } ?>
`全局异常处理器
php <?php // 设置全局异常处理(捕获未被 catch 的异常) set_exception_handler(function (Throwable $e) { $message = sprintf( "[%s] %s in %s:%d\nStack trace:\n%s", date('Y-m-d H:i:s'), $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString() );`error_log($message);
// 生产环境显示友好页面 http_response_code(500); echo "服务器内部错误,请稍后重试。"; });
// 设置全局错误处理器 set_error_handler(function (int $errno, string $errstr, string $file, int $line): bool { if (!(error_reporting() & $errno)) return false; throw new ErrorException($errstr, 0, $errno, $file, $line); }); ?>
`异常链
php <?php function connectDB(): void { try { // 模拟连接失败 throw new \RuntimeException("Connection refused"); } catch (\RuntimeException $e) { // 包装成业务异常,保留原始异常 throw new DatabaseException("数据库连接失败", 'db_connect', 500, $e); } }`try { connectDB(); } catch (DatabaseException $e) { echo $e->getMessage(); // 数据库连接失败 echo $e->getPrevious()->getMessage(); // Connection refused(原始原因) } ?>
`实用错误处理模式
php <?php // Result 模式(避免异常滥用) class Result { private function __construct( private readonly bool $success, private readonly mixed $value, private readonly string $error = '' ) {}`public static function ok(mixed $value): static { return new static(true, $value); }
public static function fail(string $error): static { return new static(false, null, $error); }
public function isOk(): bool { return $this->success; } public function getValue(): mixed { return $this->value; } public function getError(): string { return $this->error; } }
function findUser(int $id): Result { if ($id <= 0) return Result::fail("ID 不合法"); // 模拟查询 return Result::ok(["id" => $id, "name" => "张三"]); }
$result = findUser(1); if ($result->isOk()) { echo $result->getValue()['name']; // 张三 } else { echo $result->getError(); } ?>
``php <?php // 简单日志函数 function logMessage(string $level, string $message, array $context = []): void { $line = sprintf( "[%s] [%s] %s %s\n", date('Y-m-d H:i:s'), strtoupper($level), $message, $context ? json_encode($context, JSON_UNESCAPED_UNICODE) : '' ); file_put_contents( __DIR__ . '/logs/app.log', $line, FILE_APPEND | LOCK_EX ); }日志记录
logMessage('info', '用户登录', ['user_id' => 1]); logMessage('error', '支付失败', ['order_id' => 'ORD-001', 'reason' => '余额不足']);