错误处理
概述
正确的错误处理对于构建健壮的PHP应用程序至关重要。本章涵盖PHP的错误处理机制、异常、自定义错误处理器、日志记录、调试技术以及在生产环境中管理错误的最佳实践。
PHP错误类型
理解错误级别
php
<?php
// 为学习目的显示所有错误类型
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 不同类型的错误
// 致命错误 - 停止脚本执行
// function_that_does_not_exist(); // 致命错误:调用未定义的函数
// 解析错误 - 语法错误
// echo "Hello World" // 解析错误:语法错误,意外的文件结束
// 警告 - 脚本继续执行
$file = fopen('nonexistent_file.txt', 'r'); // 警告:fopen():没有这样的文件或目录
// 注意 - 脚本继续执行
echo $undefined_variable; // 注意:未定义的变量
// 严格标准 - 代码建议
// class MyClass {
// function myMethod() {} // 应该是public、protected或private
// }
// 用户生成的错误
trigger_error("这是一个用户错误", E_USER_ERROR);
trigger_error("这是一个用户警告", E_USER_WARNING);
trigger_error("这是一个用户通知", E_USER_NOTICE);
// 错误常量
echo "E_ERROR: " . E_ERROR . "\n"; // 1
echo "E_WARNING: " . E_WARNING . "\n"; // 2
echo "E_PARSE: " . E_PARSE . "\n"; // 4
echo "E_NOTICE: " . E_NOTICE . "\n"; // 8
echo "E_STRICT: " . E_STRICT . "\n"; // 2048
echo "E_ALL: " . E_ALL . "\n"; // 所有错误
?>错误报告配置
php
<?php
// 开发环境 - 显示所有错误
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// 生产环境 - 向用户隐藏错误
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/error.log');
// 自定义错误报告级别
error_reporting(E_ERROR | E_WARNING | E_PARSE); // 仅显示关键错误
error_reporting(E_ALL & ~E_NOTICE); // 除了通知以外的所有错误
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT); // 除了通知和严格模式以外的所有错误
// 检查当前错误报告级别
$currentLevel = error_reporting();
echo "当前错误报告级别:$currentLevel\n";
// 临时禁用错误报告
$oldLevel = error_reporting(0);
// 可能生成错误的代码
error_reporting($oldLevel); // 恢复之前的级别
// 获取错误级别名称的函数
function getErrorLevelName($level) {
$levels = [
E_ERROR => 'E_ERROR',
E_WARNING => 'E_WARNING',
E_PARSE => 'E_PARSE',
E_NOTICE => 'E_NOTICE',
E_CORE_ERROR => 'E_CORE_ERROR',
E_CORE_WARNING => 'E_CORE_WARNING',
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
E_USER_ERROR => 'E_USER_ERROR',
E_USER_WARNING => 'E_USER_WARNING',
E_USER_NOTICE => 'E_USER_NOTICE',
E_STRICT => 'E_STRICT',
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED'
];
return $levels[$level] ?? 'UNKNOWN';
}
?>异常处理
基本Try-Catch块
php
<?php
// 基本异常处理
try {
$result = 10 / 0; // 这在PHP中是可以的(返回INF)
echo "结果:$result\n";
// 抛出自定义异常
throw new Exception("出现了一些问题!");
} catch (Exception $e) {
echo "捕获到异常:" . $e->getMessage() . "\n";
echo "文件:" . $e->getFile() . "\n";
echo "行号:" . $e->getLine() . "\n";
}
// 多个catch块
try {
$data = json_decode('{"invalid": json}');
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidArgumentException("无效的JSON数据");
}
$file = fopen('nonexistent.txt', 'r');
if (!$file) {
throw new RuntimeException("无法打开文件");
}
} catch (InvalidArgumentException $e) {
echo "无效参数:" . $e->getMessage() . "\n";
} catch (RuntimeException $e) {
echo "运行时错误:" . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "通用异常:" . $e->getMessage() . "\n";
}
// Finally块(PHP 5.5+)
try {
$file = fopen('data.txt', 'r');
// 处理文件
throw new Exception("处理错误");
} catch (Exception $e) {
echo "错误:" . $e->getMessage() . "\n";
} finally {
// 这里总是执行
if (isset($file) && $file) {
fclose($file);
echo "文件已关闭\n";
}
}
?>自定义异常类
php
<?php
// 基本自定义异常
class AppException extends Exception {
protected $context = [];
public function __construct($message = "", $code = 0, Exception $previous = null, array $context = []) {
parent::__construct($message, $code, $previous);
$this->context = $context;
}
public function getContext() {
return $this->context;
}
public function getFullMessage() {
$message = $this->getMessage();
if (!empty($this->context)) {
$message .= " 上下文:" . json_encode($this->context);
}
return $message;
}
}
// 特定异常类型
class ValidationException extends AppException {
private $errors = [];
public function __construct($errors, $message = "验证失败") {
$this->errors = $errors;
parent::__construct($message, 400, null, ['errors' => $errors]);
}
public function getErrors() {
return $this->errors;
}
}
class DatabaseException extends AppException {
public function __construct($message, $query = null, Exception $previous = null) {
$context = [];
if ($query) {
$context['query'] = $query;
}
parent::__construct($message, 500, $previous, $context);
}
}
class AuthenticationException extends AppException {
public function __construct($message = "认证失败") {
parent::__construct($message, 401);
}
}
class AuthorizationException extends AppException {
public function __construct($message = "访问被拒绝") {
parent::__construct($message, 403);
}
}
// Usage examples
function validateUser($data) {
$errors = [];
if (empty($data['name'])) {
$errors['name'] = '姓名是必需的';
}
if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = '有效的邮箱是必需的';
}
if (!empty($errors)) {
throw new ValidationException($errors);
}
return true;
}
function authenticateUser($username, $password) {
// 模拟认证
if ($username !== 'admin' || $password !== 'secret') {
throw new AuthenticationException("无效的用户名或密码");
}
return ['id' => 1, 'username' => $username, 'role' => 'admin'];
}
function checkPermission($user, $resource) {
if ($user['role'] !== 'admin') {
throw new AuthorizationException("访问$resource需要管理员权限");
}
}
// 异常处理实战
try {
$userData = ['name' => '', 'email' => 'invalid-email'];
validateUser($userData);
} catch (ValidationException $e) {
echo "验证错误:\n";
foreach ($e->getErrors() as $field => $error) {
echo "- $field: $error\n";
}
}
try {
$user = authenticateUser('user', 'wrong');
checkPermission($user, 'admin_panel');
} catch (AuthenticationException $e) {
echo "认证错误:" . $e->getMessage() . "\n";
} catch (AuthorizationException $e) {
echo "权限错误:" . $e->getMessage() . "\n";
}
?>异常链和重新抛出
php
<?php
class DataProcessor {
public function processFile($filename) {
try {
$data = $this->readFile($filename);
return $this->parseData($data);
} catch (Exception $e) {
// 重新抛出并添加额外上下文
throw new RuntimeException(
"处理文件失败:$filename",
0,
$e // 之前的异常
);
}
}
private function readFile($filename) {
try {
if (!file_exists($filename)) {
throw new InvalidArgumentException("文件不存在:$filename");
}
$content = file_get_contents($filename);
if ($content === false) {
throw new RuntimeException("无法读取文件:$filename");
}
return $content;
} catch (Exception $e) {
// 添加更多上下文并重新抛出
throw new RuntimeException(
"文件读取失败",
0,
$e
);
}
}
private function parseData($data) {
$parsed = json_decode($data, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidArgumentException(
"无效的JSON数据:" . json_last_error_msg()
);
}
return $parsed;
}
}
// 使用异常追踪
$processor = new DataProcessor();
try {
$result = $processor->processFile('nonexistent.json');
} catch (Exception $e) {
echo "主要错误:" . $e->getMessage() . "\n";
// 通过异常链进行追踪
$current = $e;
$level = 0;
while ($current !== null) {
echo str_repeat(" ", $level) . "级别$level:" . $current->getMessage() . "\n";
echo str_repeat(" ", $level) . "文件:" . $current->getFile() . ":" . $current->getLine() . "\n";
$current = $current->getPrevious();
$level++;
}
}
?>自定义错误处理器
设置错误处理器
php
<?php
class ErrorHandler {
private $logFile;
private $displayErrors;
public function __construct($logFile = 'error.log', $displayErrors = false) {
$this->logFile = $logFile;
$this->displayErrors = $displayErrors;
}
public function register() {
set_error_handler([$this, 'handleError']);
set_exception_handler([$this, 'handleException']);
register_shutdown_function([$this, 'handleShutdown']);
}
public function handleError($severity, $message, $file, $line) {
// 不处理用@抑制的错误
if (!(error_reporting() & $severity)) {
return false;
}
$errorInfo = [
'type' => 'PHP错误',
'severity' => $this->getSeverityName($severity),
'message' => $message,
'file' => $file,
'line' => $line,
'timestamp' => date('Y-m-d H:i:s'),
'url' => $_SERVER['REQUEST_URI'] ?? 'CLI',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'CLI'
];
$this->logError($errorInfo);
if ($this->displayErrors) {
$this->displayError($errorInfo);
}
// 将错误转换为异常处理致命错误
if ($severity === E_ERROR || $severity === E_CORE_ERROR || $severity === E_COMPILE_ERROR) {
throw new ErrorException($message, 0, $severity, $file, $line);
}
return true; // 不执行PHP内部错误处理器
}
public function handleException($exception) {
$errorInfo = [
'type' => '未捕获异常',
'class' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
'timestamp' => date('Y-m-d H:i:s'),
'url' => $_SERVER['REQUEST_URI'] ?? 'CLI'
];
$this->logError($errorInfo);
if ($this->displayErrors) {
$this->displayException($errorInfo);
} else {
$this->displayGenericError();
}
}
public function handleShutdown() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) {
$errorInfo = [
'type' => '致命错误',
'severity' => $this->getSeverityName($error['type']),
'message' => $error['message'],
'file' => $error['file'],
'line' => $error['line'],
'timestamp' => date('Y-m-d H:i:s')
];
$this->logError($errorInfo);
if ($this->displayErrors) {
$this->displayError($errorInfo);
} else {
$this->displayGenericError();
}
}
}
private function logError($errorInfo) {
$logEntry = "[{$errorInfo['timestamp']}] {$errorInfo['type']}: {$errorInfo['message']} " .
"in {$errorInfo['file']} on line {$errorInfo['line']}\n";
if (isset($errorInfo['trace'])) {
$logEntry .= "堆栈追踪:\n{$errorInfo['trace']}\n";
}
$logEntry .= str_repeat('-', 80) . "\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
private function displayError($errorInfo) {
if (php_sapi_name() === 'cli') {
echo "\n{$errorInfo['type']}: {$errorInfo['message']}\n";
echo "文件:{$errorInfo['file']}:{$errorInfo['line']}\n\n";
} else {
echo "<div style='background: #ffebee; border: 1px solid #f44336; padding: 10px; margin: 10px;'>";
echo "<strong>{$errorInfo['type']}:</strong> {$errorInfo['message']}<br>";
echo "<strong>文件:</strong> {$errorInfo['file']}:{$errorInfo['line']}";
echo "</div>";
}
}
private function displayException($errorInfo) {
if (php_sapi_name() === 'cli') {
echo "\n未捕获{$errorInfo['class']}: {$errorInfo['message']}\n";
echo "文件:{$errorInfo['file']}:{$errorInfo['line']}\n";
echo "堆栈追踪:\n{$errorInfo['trace']}\n\n";
} else {
echo "<div style='background: #ffebee; border: 1px solid #f44336; padding: 15px; margin: 10px;'>";
echo "<h3>未捕获{$errorInfo['class']}</h3>";
echo "<p><strong>消息:</strong> {$errorInfo['message']}</p>";
echo "<p><strong>文件:</strong> {$errorInfo['file']}:{$errorInfo['line']}</p>";
echo "<details><summary>堆栈追踪</summary><pre>{$errorInfo['trace']}</pre></details>";
echo "</div>";
}
}
private function displayGenericError() {
if (php_sapi_name() !== 'cli') {
http_response_code(500);
echo "<h1>内部服务器错误</h1>";
echo "<p>处理您的请求时发生错误。请稍后再试。</p>";
} else {
echo "发生内部错误。\n";
}
}
private function getSeverityName($severity) {
$severities = [
E_ERROR => 'E_ERROR',
E_WARNING => 'E_WARNING',
E_PARSE => 'E_PARSE',
E_NOTICE => 'E_NOTICE',
E_CORE_ERROR => 'E_CORE_ERROR',
E_CORE_WARNING => 'E_CORE_WARNING',
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
E_USER_ERROR => 'E_USER_ERROR',
E_USER_WARNING => 'E_USER_WARNING',
E_USER_NOTICE => 'E_USER_NOTICE',
E_STRICT => 'E_STRICT',
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED'
];
return $severities[$severity] ?? 'UNKNOWN';
}
}
// 使用
$errorHandler = new ErrorHandler('app_errors.log', true); // 在开发中显示错误
$errorHandler->register();
// 测试错误处理器
echo $undefinedVariable; // 注意
trigger_error("自定义警告", E_USER_WARNING); // 警告
throw new Exception("测试异常"); // 异常
?>日志系统
简单日志器实现
php
<?php
class Logger {
const LEVEL_DEBUG = 1;
const LEVEL_INFO = 2;
const LEVEL_WARNING = 3;
const LEVEL_ERROR = 4;
const LEVEL_CRITICAL = 5;
private $logFile;
private $minLevel;
private $maxFileSize;
public function __construct($logFile = 'app.log', $minLevel = self::LEVEL_INFO, $maxFileSize = 10485760) {
$this->logFile = $logFile;
$this->minLevel = $minLevel;
$this->maxFileSize = $maxFileSize; // 默认10MB
}
public function debug($message, array $context = []) {
$this->log(self::LEVEL_DEBUG, $message, $context);
}
public function info($message, array $context = []) {
$this->log(self::LEVEL_INFO, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(self::LEVEL_WARNING, $message, $context);
}
public function error($message, array $context = []) {
$this->log(self::LEVEL_ERROR, $message, $context);
}
public function critical($message, array $context = []) {
$this->log(self::LEVEL_CRITICAL, $message, $context);
}
public function log($level, $message, array $context = []) {
if ($level < $this->minLevel) {
return;
}
$this->rotateLogIfNeeded();
$levelName = $this->getLevelName($level);
$timestamp = date('Y-m-d H:i:s');
$contextStr = !empty($context) ? ' ' . json_encode($context) : '';
$logEntry = "[$timestamp] [$levelName] $message$contextStr\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
public function logException(Exception $exception, $level = self::LEVEL_ERROR) {
$context = [
'exception' => get_class($exception),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString()
];
$this->log($level, $exception->getMessage(), $context);
}
private function rotateLogIfNeeded() {
if (!file_exists($this->logFile)) {
return;
}
if (filesize($this->logFile) > $this->maxFileSize) {
$backupFile = $this->logFile . '.' . date('Y-m-d-H-i-s');
rename($this->logFile, $backupFile);
}
}
private function getLevelName($level) {
$levels = [
self::LEVEL_DEBUG => 'DEBUG',
self::LEVEL_INFO => 'INFO',
self::LEVEL_WARNING => 'WARNING',
self::LEVEL_ERROR => 'ERROR',
self::LEVEL_CRITICAL => 'CRITICAL'
];
return $levels[$level] ?? 'UNKNOWN';
}
}
// Advanced logger with multiple handlers
class MultiLogger {
private $handlers = [];
public function addHandler(LoggerInterface $handler) {
$this->handlers[] = $handler;
}
public function log($level, $message, array $context = []) {
foreach ($this->handlers as $handler) {
$handler->log($level, $message, $context);
}
}
// 委托方法
public function debug($message, array $context = []) {
$this->log(Logger::LEVEL_DEBUG, $message, $context);
}
public function info($message, array $context = []) {
$this->log(Logger::LEVEL_INFO, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(Logger::LEVEL_WARNING, $message, $context);
}
public function error($message, array $context = []) {
$this->log(Logger::LEVEL_ERROR, $message, $context);
}
public function critical($message, array $context = []) {
$this->log(Logger::LEVEL_CRITICAL, $message, $context);
}
}
interface LoggerInterface {
public function log($level, $message, array $context = []);
}
class FileLoggerHandler implements LoggerInterface {
private $logger;
public function __construct($logFile) {
$this->logger = new Logger($logFile);
}
public function log($level, $message, array $context = []) {
$this->logger->log($level, $message, $context);
}
}
class EmailLoggerHandler implements LoggerInterface {
private $email;
private $minLevel;
public function __construct($email, $minLevel = Logger::LEVEL_ERROR) {
$this->email = $email;
$this->minLevel = $minLevel;
}
public function log($level, $message, array $context = []) {
if ($level >= $this->minLevel) {
$subject = "Application Error - Level $level";
$body = "Message: $message\n";
$body .= "Context: " . json_encode($context, JSON_PRETTY_PRINT);
$body .= "\nTime: " . date('Y-m-d H:i:s');
mail($this->email, $subject, $body);
}
}
}
// 使用
$logger = new MultiLogger();
$logger->addHandler(new FileLoggerHandler('app.log'));
$logger->addHandler(new EmailLoggerHandler('admin@example.com'));
$logger->info('应用程序已启动');
$logger->warning('磁盘空间不足', ['available' => '500MB']);
$logger->error('数据库连接失败', ['host' => 'localhost', 'port' => 3306]);
?>调试技术
调试信息和回溯
php
<?php
class Debugger {
public static function dump($variable, $label = null) {
if ($label) {
echo "<h4>$label</h4>";
}
echo "<pre>";
var_dump($variable);
echo "</pre>";
}
public static function backtrace($limit = 10) {
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit);
echo "<h4>堆栈追踪:</h4>";
echo "<ol>";
foreach ($trace as $i => $frame) {
$file = $frame['file'] ?? '未知';
$line = $frame['line'] ?? '未知';
$function = $frame['function'] ?? '未知';
$class = $frame['class'] ?? '';
$type = $frame['type'] ?? '';
echo "<li>";
echo "<strong>$class$type$function()</strong><br>";
echo "文件:$file:$line";
echo "</li>";
}
echo "</ol>";
}
public static function printMemoryUsage() {
$memory = memory_get_usage(true);
$peak = memory_get_peak_usage(true);
echo "当前内存使用:" . self::formatBytes($memory) . "\n";
echo "峰值内存使用:" . self::formatBytes($peak) . "\n";
}
public static function printExecutionTime($startTime = null) {
static $start;
if ($startTime !== null) {
$start = $startTime;
return;
}
if ($start === null) {
$start = microtime(true);
return;
}
$end = microtime(true);
$execution = $end - $start;
echo "执行时间:" . number_format($execution, 4) . " 秒\n";
}
public static function profileFunction($callback, $iterations = 1) {
$startTime = microtime(true);
$startMemory = memory_get_usage();
for ($i = 0; $i < $iterations; $i++) {
call_user_func($callback);
}
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = $endTime - $startTime;
$memoryUsed = $endMemory - $startMemory;
echo "函数性能分析结果:\n";
echo "迭代次数:$iterations\n";
echo "总时间:" . number_format($executionTime, 4) . " 秒\n";
echo "平均时间:" . number_format($executionTime / $iterations, 6) . " 秒\n";
echo "内存使用:" . self::formatBytes($memoryUsed) . "\n";
}
private static function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, 2) . ' ' . $units[$i];
}
}
// 调试辅助函数
function dd($variable, $label = null) {
Debugger::dump($variable, $label);
die();
}
function bt($limit = 10) {
Debugger::backtrace($limit);
}
// 使用示例
$data = ['name' => '张三', 'age' => 30, 'hobbies' => ['阅读', '编程']];
Debugger::dump($data, '用户数据');
function testFunction() {
function nestedFunction() {
Debugger::backtrace();
}
nestedFunction();
}
testFunction();
// 分析函数
Debugger::profileFunction(function() {
$sum = 0;
for ($i = 0; $i < 100000; $i++) {
$sum += $i;
}
return $sum;
}, 10);
Debugger::printMemoryUsage();
?>断言和测试助手
php
<?php
class Assert {
public static function true($condition, $message = '断言失败') {
if (!$condition) {
throw new AssertionError($message);
}
}
public static function false($condition, $message = '断言失败') {
if ($condition) {
throw new AssertionError($message);
}
}
public static function equals($expected, $actual, $message = '值不相等') {
if ($expected !== $actual) {
$message .= "。期望值:" . var_export($expected, true) .
",实际值:" . var_export($actual, true);
throw new AssertionError($message);
}
}
public static function notNull($value, $message = '值为空') {
if ($value === null) {
throw new AssertionError($message);
}
}
public static function instanceOf($object, $class, $message = '对象不是类的实例') {
if (!($object instanceof $class)) {
$actualClass = is_object($object) ? get_class($object) : gettype($object);
$message .= "。期望:$class,实际:$actualClass";
throw new AssertionError($message);
}
}
public static function throws($callback, $expectedException = Exception::class, $message = '期望的异常未被抛出') {
try {
call_user_func($callback);
throw new AssertionError($message);
} catch (Exception $e) {
if (!($e instanceof $expectedException)) {
$actualClass = get_class($e);
throw new AssertionError("期望$expectedException,得到$actualClass:" . $e->getMessage());
}
}
}
}
class AssertionError extends Exception {}
// 测试辅助类
class TestRunner {
private $tests = [];
private $passed = 0;
private $failed = 0;
public function addTest($name, $callback) {
$this->tests[$name] = $callback;
}
public function run() {
echo "运行测试…\n\n";
foreach ($this->tests as $name => $callback) {
try {
call_user_func($callback);
echo "✓ $name\n";
$this->passed++;
} catch (Exception $e) {
echo "✗ $name: " . $e->getMessage() . "\n";
$this->failed++;
}
}
echo "\n结果:{$this->passed}个通过,{$this->failed}个失败\n";
}
}
// 示例使用
$runner = new TestRunner();
$runner->addTest('测试基本数学', function() {
Assert::equals(4, 2 + 2, '加法应该有效');
Assert::equals(0, 2 - 2, '减法应该有效');
});
$runner->addTest('测试字符串操作', function() {
Assert::equals('你好世界', '你好' . '世界', '字符串连接');
Assert::equals(2, mb_strlen('你好'), '字符串长度');
});
$runner->addTest('测试异常抛出', function() {
Assert::throws(function() {
throw new InvalidArgumentException('测试异常');
}, InvalidArgumentException::class);
});
$runner->run();
?>生产错误处理
错误监控和警报
php
<?php
class ErrorMonitor {
private $config;
private $logger;
public function __construct($config) {
$this->config = $config;
$this->logger = new Logger($config['log_file']);
}
public function handleError($error) {
// 记录错误
$this->logger->error($error['message'], $error);
// 检查是否应该警报
if ($this->shouldAlert($error)) {
$this->sendAlert($error);
}
// 更新错误统计
$this->updateStats($error);
}
private function shouldAlert($error) {
// 对关键错误进行警报
if ($error['severity'] >= Logger::LEVEL_CRITICAL) {
return true;
}
// 如果错误率过高则警报
$recentErrors = $this->getRecentErrorCount();
if ($recentErrors > $this->config['max_errors_per_minute']) {
return true;
}
// 对特定错误模式进行警报
foreach ($this->config['alert_patterns'] as $pattern) {
if (strpos($error['message'], $pattern) !== false) {
return true;
}
}
return false;
}
private function sendAlert($error) {
$alertData = [
'timestamp' => date('Y-m-d H:i:s'),
'error' => $error,
'server' => $_SERVER['SERVER_NAME'] ?? 'CLI',
'url' => $_SERVER['REQUEST_URI'] ?? 'CLI'
];
// 发送邮件警报
if ($this->config['email_alerts']) {
$this->sendEmailAlert($alertData);
}
// 发送到监控服务(例如Slack、PagerDuty)
if ($this->config['webhook_url']) {
$this->sendWebhookAlert($alertData);
}
}
private function sendEmailAlert($alertData) {
$subject = "关键错误警报 - " . $alertData['server'];
$body = "发生了关键错误:\n\n";
$body .= "时间:" . $alertData['timestamp'] . "\n";
$body .= "消息:" . $alertData['error']['message'] . "\n";
$body .= "文件:" . $alertData['error']['file'] . ":" . $alertData['error']['line'] . "\n";
$body .= "URL:" . $alertData['url'] . "\n";
mail($this->config['alert_email'], $subject, $body);
}
private function sendWebhookAlert($alertData) {
$payload = json_encode([
'text' => '关键错误警报',
'attachments' => [
[
'color' => 'danger',
'fields' => [
['title' => '消息', 'value' => $alertData['error']['message'], 'short' => false],
['title' => '文件', 'value' => $alertData['error']['file'] . ':' . $alertData['error']['line'], 'short' => true],
['title' => '服务器', 'value' => $alertData['server'], 'short' => true]
]
]
]
]);
$ch = curl_init($this->config['webhook_url']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
private function getRecentErrorCount() {
// 实现将检查上一分钟内的错误数
// 这可以使用数据库、缓存或日志文件分析
return 0; // 占位符
}
private function updateStats($error) {
// 更新监控仪表盘的错误统计
// 这可以写入数据库或指标服务
}
}
// 配置
$errorConfig = [
'log_file' => 'errors.log',
'email_alerts' => true,
'alert_email' => 'admin@example.com',
'webhook_url' => 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK',
'max_errors_per_minute' => 10,
'alert_patterns' => ['数据库', '支付', '认证']
];
$monitor = new ErrorMonitor($errorConfig);
// 与错误处理器集成
set_exception_handler(function($exception) use ($monitor) {
$error = [
'severity' => Logger::LEVEL_CRITICAL,
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString()
];
$monitor->handleError($error);
// 显示用户友好的错误页面
if (!headers_sent()) {
http_response_code(500);
include 'error_pages/500.html';
}
});
?>下一步
现在您已经了解了错误处理,让我们在文件处理中探索文件处理。
实践练习
- 为Web应用程序创建一个全面的错误处理系统
- 构建一个具有多种输出格式(文件、数据库、邮件)的日志系统
- 为不同类型的应用程序错误实现自定义异常类
- 创建一个具有性能分析和内存监控的调试工具包
- 为生产应用程序设置错误监控和警报
正确的错误处理对于构建健壮、可维护的PHP应用程序至关重要!