PHP 错误处理:从 try/catch 到工业级解决方案
\n\n每位 PHP 开发者都会遇到错误。基础的 try/catch 只是冰山一角。本文将深入探讨高级错误处理技术,将脆弱的代码转变为能够正确应对任何故障的可靠系统。
1. 深入理解 try/catch:finally 块与多重 catch
\n\n标准的 try/catch 是基础,但许多人忘记了 finally 块,它无论是否发生异常都会执行。这是关闭资源(文件、数据库连接)的理想位置。
<?php\nfunction processFile(string $filename): void {\n $handle = fopen($filename, 'r');\n if (!$handle) {\n throw new \\RuntimeException("无法打开文件: $filename");\n }\n \n try {\n // 处理文件\n while (($line = fgets($handle)) !== false) {\n // ... 处理逻辑\n }\n } catch (\\RuntimeException $e) {\n // 特定于输入/输出错误的处理\n error_log("读取文件错误: " . $e->getMessage());\n throw $e; // 向上抛出\n } catch (\\Exception $e) {\n // 处理任何其他异常\n error_log("未知错误: " . $e->getMessage());\n throw $e;\n } finally {\n fclose($handle); // 始终关闭文件\n }\n}\n?>\n\n重要提示: catch 块的顺序很重要。更具体的异常(例如 \\RuntimeException)应放在通用异常(\\Exception)之前。否则,特定的块将永远不会被执行。
2. 自定义异常类:语义与层次结构
\n\n内置异常(PDOException、RuntimeException)通常信息不足。创建您自己的异常层次结构,以便按类型区分错误并简化其处理。
\n\n<?php\n// 应用程序基础异常\nclass AppException extends \\Exception {}\n\n// 验证错误\nclass ValidationException extends AppException {\n private array $errors;\n \n public function __construct(array $errors, string $message = "验证错误") {\n parent::__construct($message, 422);\n $this->errors = $errors;\n }\n \n public function getErrors(): array {\n return $this->errors;\n }\n}\n\n// 访问错误\nclass AccessDeniedException extends AppException {\n public function __construct(string $message = "访问被拒绝") {\n parent::__construct($message, 403);\n }\n}\n\n// 使用示例\nfunction registerUser(array $data): void {\n $errors = [];\n \n if (empty($data['email'])) {\n $errors['email'] = '电子邮件为必填项';\n }\n if (empty($data['password'])) {\n $errors['password'] = '密码为必填项';\n }\n \n if (!empty($errors)) {\n throw new ValidationException($errors);\n }\n \n // 注册逻辑...\n}\n\ntry {\n registerUser(['email' => '']);\n} catch (ValidationException $e) {\n // 特定处理:返回包含错误的 JSON\n http_response_code(422);\n echo json_encode(['errors' => $e->getErrors()]);\n} catch (AccessDeniedException $e) {\n http_response_code(403);\n echo json_encode(['error' => $e->getMessage()]);\n} catch (AppException $e) {\n // 对所有其他应用程序错误的通用处理\n http_response_code(500);\n echo json_encode(['error' => '服务器内部错误']);\n}\n?>\n\n建议: 使用 HTTP 状态码(400、403、422、500)作为异常代码——这简化了与 REST API 的集成。
\n\n3. 全局错误处理器:ErrorHandler 和 ShutdownHandler
\n\n并非所有错误都能通过 try/catch 捕获。致命错误(E_ERROR)、警告(E_WARNING)和解析错误