错误处理是PHP开发中的重要环节,良好的错误处理机制能够提高应用程序的稳定性和用户体验。本章将详细介绍PHP中的错误类型、异常处理机制、自定义异常、调试技巧以及最佳实践。
8.1 PHP错误类型
8.1.1 错误级别
<?php
// PHP错误级别演示
echo "=== PHP错误级别演示 ===\n";
// 1. 致命错误 (Fatal Error) - E_ERROR
// 这些错误会导致脚本停止执行
// function fatalErrorDemo() {
// call_to_undefined_function(); // 调用未定义函数
// }
// fatalErrorDemo();
// 2. 警告 (Warning) - E_WARNING
// 脚本继续执行,但会显示警告信息
echo "--- 警告演示 ---\n";
include 'non_existent_file.php'; // 包含不存在的文件
echo "警告后脚本继续执行\n";
// 3. 注意 (Notice) - E_NOTICE
// 脚本继续执行,显示注意信息
echo "\n--- 注意演示 ---\n";
echo $undefined_variable; // 使用未定义变量
echo "注意后脚本继续执行\n";
// 4. 解析错误 (Parse Error) - E_PARSE
// 语法错误,脚本无法运行
// echo "解析错误演示
// 缺少引号会导致解析错误
// 5. 严格标准 (Strict Standards) - E_STRICT
// PHP 7+ 中的严格类型检查
declare(strict_types=1);
function strictDemo(int $number): string {
return (string)$number;
}
echo "\n--- 严格模式演示 ---\n";
echo strictDemo(123) . "\n";
// echo strictDemo("123"); // 在严格模式下会报错
// 6. 用户错误 - E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
echo "\n--- 用户自定义错误 ---\n";
trigger_error("这是用户定义的错误", E_USER_ERROR);
?>
8.1.2 错误报告设置
<?php
// 错误报告配置
echo "=== 错误报告配置 ===\n";
// 获取当前错误报告级别
echo "当前错误报告级别:" . error_reporting() . "\n";
// 显示所有错误
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// 只显示致命错误和警告
// error_reporting(E_ERROR | E_WARNING);
// 关闭错误显示(生产环境推荐)
// error_reporting(0);
// ini_set('display_errors', 0);
// 记录错误到日志文件
ini_set('log_errors', 1);
ini_set('error_log', 'php_errors.log');
// 自定义错误处理函数
function customErrorHandler($errno, $errstr, $errfile, $errline) {
$errorTypes = [
E_ERROR => 'Fatal Error',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Strict Standards',
E_RECOVERABLE_ERROR => 'Recoverable Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated'
];
$errorType = isset($errorTypes[$errno]) ? $errorTypes[$errno] : 'Unknown Error';
$message = "[$errorType] $errstr in $errfile on line $errline";
// 记录到日志
error_log($message);
// 显示友好的错误信息
echo "<div style='color: red; border: 1px solid red; padding: 10px; margin: 5px;'>";
echo "<strong>$errorType:</strong> $errstr<br>";
echo "<small>File: $errfile, Line: $errline</small>";
echo "</div>";
// 对于致命错误,停止执行
if ($errno === E_USER_ERROR) {
exit(1);
}
return true; // 阻止PHP内置错误处理
}
// 注册自定义错误处理器
set_error_handler('customErrorHandler');
// 测试自定义错误处理
echo "\n--- 自定义错误处理测试 ---\n";
echo $undefined_var; // 触发Notice
trigger_error("自定义警告消息", E_USER_WARNING);
// 恢复默认错误处理
restore_error_handler();
?>
8.1.3 错误处理最佳实践
<?php
// 错误处理最佳实践
class ErrorHandler {
private $logFile;
private $displayErrors;
public function __construct($logFile = 'app_errors.log', $displayErrors = false) {
$this->logFile = $logFile;
$this->displayErrors = $displayErrors;
// 注册错误和异常处理器
set_error_handler([$this, 'handleError']);
set_exception_handler([$this, 'handleException']);
register_shutdown_function([$this, 'handleFatalError']);
}
/**
* 处理一般错误
*/
public function handleError($errno, $errstr, $errfile, $errline) {
// 如果错误被@操作符抑制,则不处理
if (!(error_reporting() & $errno)) {
return false;
}
$errorInfo = [
'type' => $this->getErrorType($errno),
'message' => $errstr,
'file' => $errfile,
'line' => $errline,
'timestamp' => date('Y-m-d H:i:s'),
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)
];
$this->logError($errorInfo);
if ($this->displayErrors) {
$this->displayError($errorInfo);
}
// 致命错误停止执行
if ($errno === E_USER_ERROR) {
exit(1);
}
return true;
}
/**
* 处理未捕获的异常
*/
public function handleException($exception) {
$errorInfo = [
'type' => 'Uncaught Exception',
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'timestamp' => date('Y-m-d H:i:s'),
'trace' => $exception->getTrace(),
'class' => get_class($exception)
];
$this->logError($errorInfo);
if ($this->displayErrors) {
$this->displayError($errorInfo);
} else {
echo "An error occurred. Please try again later.";
}
}
/**
* 处理致命错误
*/
public function handleFatalError() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
$errorInfo = [
'type' => 'Fatal Error',
'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 {
echo "A fatal error occurred. Please contact support.";
}
}
}
/**
* 记录错误到日志文件
*/
private function logError($errorInfo) {
$logMessage = sprintf(
"[%s] %s: %s in %s on line %d\n",
$errorInfo['timestamp'],
$errorInfo['type'],
$errorInfo['message'],
$errorInfo['file'],
$errorInfo['line']
);
if (isset($errorInfo['trace'])) {
$logMessage .= "Stack trace:\n";
foreach ($errorInfo['trace'] as $i => $frame) {
$logMessage .= sprintf(
"#%d %s(%d): %s%s%s()\n",
$i,
$frame['file'] ?? '[internal function]',
$frame['line'] ?? 0,
$frame['class'] ?? '',
$frame['type'] ?? '',
$frame['function'] ?? ''
);
}
}
$logMessage .= "\n";
file_put_contents($this->logFile, $logMessage, FILE_APPEND | LOCK_EX);
}
/**
* 显示错误信息
*/
private function displayError($errorInfo) {
echo "<div style='background: #ffebee; border: 1px solid #f44336; padding: 15px; margin: 10px 0; border-radius: 4px;'>";
echo "<h3 style='color: #d32f2f; margin: 0 0 10px 0;'>{$errorInfo['type']}</h3>";
echo "<p><strong>Message:</strong> {$errorInfo['message']}</p>";
echo "<p><strong>File:</strong> {$errorInfo['file']}</p>";
echo "<p><strong>Line:</strong> {$errorInfo['line']}</p>";
echo "<p><strong>Time:</strong> {$errorInfo['timestamp']}</p>";
if (isset($errorInfo['trace']) && !empty($errorInfo['trace'])) {
echo "<details><summary><strong>Stack Trace</strong></summary>";
echo "<pre style='background: #f5f5f5; padding: 10px; overflow-x: auto;'>";
foreach ($errorInfo['trace'] as $i => $frame) {
printf(
"#%d %s(%d): %s%s%s()\n",
$i,
$frame['file'] ?? '[internal function]',
$frame['line'] ?? 0,
$frame['class'] ?? '',
$frame['type'] ?? '',
$frame['function'] ?? ''
);
}
echo "</pre></details>";
}
echo "</div>";
}
/**
* 获取错误类型名称
*/
private function getErrorType($errno) {
$errorTypes = [
E_ERROR => 'Fatal Error',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Strict Standards',
E_RECOVERABLE_ERROR => 'Recoverable Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated'
];
return isset($errorTypes[$errno]) ? $errorTypes[$errno] : 'Unknown Error';
}
}
// 使用错误处理器
echo "=== 错误处理器演示 ===\n";
// 开发环境 - 显示错误
$errorHandler = new ErrorHandler('dev_errors.log', true);
// 生产环境 - 不显示错误
// $errorHandler = new ErrorHandler('prod_errors.log', false);
// 测试错误处理
echo "测试Notice错误:";
echo $undefined_variable;
echo "\n测试Warning错误:";
include 'non_existent_file.php';
echo "\n测试用户错误:";
trigger_error("这是一个用户定义的错误", E_USER_WARNING);
?>
8.2 异常处理机制
8.2.1 异常基础
<?php
// 异常处理基础
echo "=== 异常处理基础 ===\n";
// 基本的try-catch结构
try {
echo "尝试执行可能出错的代码\n";
// 抛出异常
throw new Exception("这是一个测试异常", 1001);
echo "这行代码不会执行\n";
} catch (Exception $e) {
echo "捕获到异常:" . $e->getMessage() . "\n";
echo "异常代码:" . $e->getCode() . "\n";
echo "异常文件:" . $e->getFile() . "\n";
echo "异常行号:" . $e->getLine() . "\n";
}
echo "异常处理后继续执行\n\n";
// try-catch-finally结构
echo "--- try-catch-finally演示 ---\n";
function testFinally($throwException = false) {
try {
echo "执行try块\n";
if ($throwException) {
throw new Exception("测试异常");
}
echo "try块正常结束\n";
return "try返回值";
} catch (Exception $e) {
echo "执行catch块:" . $e->getMessage() . "\n";
return "catch返回值";
} finally {
echo "执行finally块(总是执行)\n";
// finally块中的return会覆盖try/catch的return
// return "finally返回值";
}
}
echo "正常情况:\n";
$result1 = testFinally(false);
echo "返回值:$result1\n\n";
echo "异常情况:\n";
$result2 = testFinally(true);
echo "返回值:$result2\n\n";
// 多个catch块
echo "--- 多个catch块演示 ---\n";
function multiCatchDemo($type) {
try {
switch ($type) {
case 'invalid':
throw new InvalidArgumentException("无效参数异常");
case 'runtime':
throw new RuntimeException("运行时异常");
case 'logic':
throw new LogicException("逻辑异常");
default:
throw new Exception("通用异常");
}
} catch (InvalidArgumentException $e) {
echo "处理无效参数异常:" . $e->getMessage() . "\n";
} catch (RuntimeException $e) {
echo "处理运行时异常:" . $e->getMessage() . "\n";
} catch (LogicException $e) {
echo "处理逻辑异常:" . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "处理通用异常:" . $e->getMessage() . "\n";
}
}
multiCatchDemo('invalid');
multiCatchDemo('runtime');
multiCatchDemo('logic');
multiCatchDemo('other');
// PHP 7.1+ 多异常类型捕获
echo "\n--- 多异常类型捕获 ---\n";
try {
throw new InvalidArgumentException("参数错误");
} catch (InvalidArgumentException | RuntimeException $e) {
echo "捕获到参数错误或运行时错误:" . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "捕获到其他异常:" . $e->getMessage() . "\n";
}
?>
8.2.2 异常传播和重新抛出
<?php
// 异常传播和重新抛出
echo "=== 异常传播演示 ===\n";
function level3() {
echo "Level 3: 抛出异常\n";
throw new Exception("来自Level 3的异常");
}
function level2() {
echo "Level 2: 调用Level 3\n";
level3();
echo "Level 2: 这行不会执行\n";
}
function level1() {
try {
echo "Level 1: 调用Level 2\n";
level2();
echo "Level 1: 这行不会执行\n";
} catch (Exception $e) {
echo "Level 1: 捕获异常 - " . $e->getMessage() . "\n";
echo "异常追踪:\n" . $e->getTraceAsString() . "\n";
}
}
level1();
echo "\n--- 异常重新抛出演示 ---\n";
class DatabaseException extends Exception {}
class ConnectionException extends DatabaseException {}
class QueryException extends DatabaseException {}
class Database {
private $connected = false;
public function connect() {
// 模拟连接失败
if (rand(0, 1)) {
throw new ConnectionException("数据库连接失败");
}
$this->connected = true;
echo "数据库连接成功\n";
}
public function query($sql) {
if (!$this->connected) {
throw new QueryException("数据库未连接");
}
// 模拟查询错误
if (strpos($sql, 'DROP') !== false) {
throw new QueryException("危险的SQL操作被阻止");
}
echo "执行查询:$sql\n";
return "查询结果";
}
}
class UserService {
private $db;
public function __construct() {
$this->db = new Database();
}
public function getUser($id) {
try {
$this->db->connect();
$result = $this->db->query("SELECT * FROM users WHERE id = $id");
return $result;
} catch (ConnectionException $e) {
// 记录连接错误并重新抛出
error_log("数据库连接错误:" . $e->getMessage());
throw new Exception("服务暂时不可用,请稍后重试", 503, $e);
} catch (QueryException $e) {
// 记录查询错误并重新抛出
error_log("查询错误:" . $e->getMessage());
throw new Exception("数据查询失败", 500, $e);
}
}
}
class UserController {
private $userService;
public function __construct() {
$this->userService = new UserService();
}
public function showUser($id) {
try {
$user = $this->userService->getUser($id);
echo "用户信息:$user\n";
} catch (Exception $e) {
echo "错误:" . $e->getMessage() . "\n";
echo "HTTP状态码:" . $e->getCode() . "\n";
// 显示原始异常信息(开发环境)
if ($e->getPrevious()) {
echo "原始错误:" . $e->getPrevious()->getMessage() . "\n";
}
}
}
}
// 测试异常传播和重新抛出
$controller = new UserController();
$controller->showUser(123);
?>
8.2.3 自定义异常类
<?php
// 自定义异常类
echo "=== 自定义异常类演示 ===\n";
// 基础自定义异常
class BaseException 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 setContext(array $context) {
$this->context = $context;
}
public function addContext($key, $value) {
$this->context[$key] = $value;
}
public function toArray() {
return [
'message' => $this->getMessage(),
'code' => $this->getCode(),
'file' => $this->getFile(),
'line' => $this->getLine(),
'context' => $this->context,
'trace' => $this->getTrace()
];
}
public function __toString() {
$output = get_class($this) . ": {$this->getMessage()}\n";
$output .= "File: {$this->getFile()}\n";
$output .= "Line: {$this->getLine()}\n";
if (!empty($this->context)) {
$output .= "Context: " . json_encode($this->context) . "\n";
}
return $output;
}
}
// 业务异常类
class ValidationException extends BaseException {
private $errors = [];
public function __construct($message = "Validation failed", array $errors = [], $code = 422) {
parent::__construct($message, $code);
$this->errors = $errors;
}
public function getErrors() {
return $this->errors;
}
public function addError($field, $message) {
$this->errors[$field][] = $message;
}
public function hasErrors() {
return !empty($this->errors);
}
}
class AuthenticationException extends BaseException {
public function __construct($message = "Authentication failed", $code = 401) {
parent::__construct($message, $code);
}
}
class AuthorizationException extends BaseException {
private $requiredPermission;
public function __construct($message = "Access denied", $requiredPermission = null, $code = 403) {
parent::__construct($message, $code);
$this->requiredPermission = $requiredPermission;
}
public function getRequiredPermission() {
return $this->requiredPermission;
}
}
class ResourceNotFoundException extends BaseException {
private $resourceType;
private $resourceId;
public function __construct($resourceType, $resourceId, $code = 404) {
$message = "$resourceType with ID '$resourceId' not found";
parent::__construct($message, $code);
$this->resourceType = $resourceType;
$this->resourceId = $resourceId;
}
public function getResourceType() {
return $this->resourceType;
}
public function getResourceId() {
return $this->resourceId;
}
}
// 使用自定义异常的示例类
class UserValidator {
public function validate(array $data) {
$errors = [];
if (empty($data['name'])) {
$errors['name'][] = '姓名不能为空';
} elseif (strlen($data['name']) < 2) {
$errors['name'][] = '姓名至少需要2个字符';
}
if (empty($data['email'])) {
$errors['email'][] = '邮箱不能为空';
} elseif (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'][] = '邮箱格式无效';
}
if (empty($data['password'])) {
$errors['password'][] = '密码不能为空';
} elseif (strlen($data['password']) < 6) {
$errors['password'][] = '密码至少需要6个字符';
}
if (!empty($errors)) {
throw new ValidationException('用户数据验证失败', $errors);
}
return true;
}
}
class AuthService {
private $users = [
'admin@example.com' => ['password' => 'admin123', 'role' => 'admin'],
'user@example.com' => ['password' => 'user123', 'role' => 'user']
];
public function authenticate($email, $password) {
if (!isset($this->users[$email])) {
throw new AuthenticationException('用户不存在');
}
if ($this->users[$email]['password'] !== $password) {
throw new AuthenticationException('密码错误');
}
return $this->users[$email];
}
public function authorize($user, $permission) {
if ($user['role'] !== 'admin' && $permission === 'admin') {
throw new AuthorizationException('需要管理员权限', $permission);
}
return true;
}
}
class UserRepository {
private $users = [
1 => ['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'],
2 => ['id' => 2, 'name' => '李四', 'email' => 'lisi@example.com']
];
public function findById($id) {
if (!isset($this->users[$id])) {
throw new ResourceNotFoundException('User', $id);
}
return $this->users[$id];
}
}
// 测试自定义异常
echo "--- 验证异常测试 ---\n";
$validator = new UserValidator();
try {
$validator->validate([
'name' => 'A',
'email' => 'invalid-email',
'password' => '123'
]);
} catch (ValidationException $e) {
echo "验证失败:" . $e->getMessage() . "\n";
echo "错误详情:\n";
foreach ($e->getErrors() as $field => $fieldErrors) {
echo " $field: " . implode(', ', $fieldErrors) . "\n";
}
}
echo "\n--- 认证异常测试 ---\n";
$authService = new AuthService();
try {
$authService->authenticate('wrong@example.com', 'wrongpassword');
} catch (AuthenticationException $e) {
echo "认证失败:" . $e->getMessage() . "\n";
echo "状态码:" . $e->getCode() . "\n";
}
echo "\n--- 授权异常测试 ---\n";
try {
$user = $authService->authenticate('user@example.com', 'user123');
$authService->authorize($user, 'admin');
} catch (AuthenticationException $e) {
echo "认证失败:" . $e->getMessage() . "\n";
} catch (AuthorizationException $e) {
echo "授权失败:" . $e->getMessage() . "\n";
echo "需要权限:" . $e->getRequiredPermission() . "\n";
}
echo "\n--- 资源未找到异常测试 ---\n";
$userRepo = new UserRepository();
try {
$userRepo->findById(999);
} catch (ResourceNotFoundException $e) {
echo "资源未找到:" . $e->getMessage() . "\n";
echo "资源类型:" . $e->getResourceType() . "\n";
echo "资源ID:" . $e->getResourceId() . "\n";
}
?>
8.3 调试技巧
8.3.1 调试函数和工具
<?php
// 调试函数和工具
echo "=== 调试函数演示 ===\n";
// 1. var_dump() - 详细输出变量信息
echo "--- var_dump演示 ---\n";
$array = ['name' => '张三', 'age' => 25, 'hobbies' => ['读书', '游泳']];
var_dump($array);
// 2. print_r() - 可读性更好的输出
echo "\n--- print_r演示 ---\n";
print_r($array);
// 3. var_export() - 输出可执行的PHP代码
echo "\n--- var_export演示 ---\n";
var_export($array);
echo "\n";
// 4. debug_backtrace() - 获取调用栈
echo "\n--- debug_backtrace演示 ---\n";
function debugLevel3() {
echo "Level 3 调用栈:\n";
$trace = debug_backtrace();
foreach ($trace as $i => $frame) {
echo "#$i {$frame['function']}() called at [{$frame['file']}:{$frame['line']}]\n";
}
}
function debugLevel2() {
debugLevel3();
}
function debugLevel1() {
debugLevel2();
}
debugLevel1();
// 5. debug_print_backtrace() - 直接打印调用栈
echo "\n--- debug_print_backtrace演示 ---\n";
function printBacktraceDemo() {
debug_print_backtrace();
}
function callPrintBacktrace() {
printBacktraceDemo();
}
callPrintBacktrace();
// 6. get_defined_vars() - 获取所有已定义变量
echo "\n--- get_defined_vars演示 ---\n";
$localVar = "本地变量";
$anotherVar = 123;
function showDefinedVars() {
$functionVar = "函数变量";
$vars = get_defined_vars();
echo "函数内定义的变量:\n";
print_r($vars);
}
showDefinedVars();
// 7. memory_get_usage() - 获取内存使用情况
echo "\n--- 内存使用情况 ---\n";
echo "当前内存使用:" . number_format(memory_get_usage()) . " bytes\n";
echo "峰值内存使用:" . number_format(memory_get_peak_usage()) . " bytes\n";
// 创建大数组测试内存使用
$bigArray = range(1, 100000);
echo "创建大数组后内存使用:" . number_format(memory_get_usage()) . " bytes\n";
echo "峰值内存使用:" . number_format(memory_get_peak_usage()) . " bytes\n";
unset($bigArray);
echo "释放数组后内存使用:" . number_format(memory_get_usage()) . " bytes\n";
?>
8.3.2 调试类和工具
<?php
// 调试工具类
class Debugger {
private static $startTime;
private static $checkpoints = [];
private static $queries = [];
private static $enabled = true;
/**
* 启用/禁用调试
*/
public static function enable($enabled = true) {
self::$enabled = $enabled;
}
/**
* 开始计时
*/
public static function start() {
if (!self::$enabled) return;
self::$startTime = microtime(true);
self::$checkpoints = [];
self::$queries = [];
}
/**
* 添加检查点
*/
public static function checkpoint($label) {
if (!self::$enabled) return;
$currentTime = microtime(true);
$elapsed = $currentTime - self::$startTime;
$memory = memory_get_usage();
self::$checkpoints[] = [
'label' => $label,
'time' => $currentTime,
'elapsed' => $elapsed,
'memory' => $memory,
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)
];
}
/**
* 记录SQL查询
*/
public static function logQuery($sql, $params = [], $executionTime = 0) {
if (!self::$enabled) return;
self::$queries[] = [
'sql' => $sql,
'params' => $params,
'execution_time' => $executionTime,
'timestamp' => microtime(true),
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
];
}
/**
* 输出调试信息
*/
public static function dump($var, $label = null) {
if (!self::$enabled) return;
echo "<div style='background: #f0f0f0; border: 1px solid #ccc; padding: 10px; margin: 5px 0; font-family: monospace;'>";
if ($label) {
echo "<strong>$label:</strong><br>";
}
echo "<pre>";
var_dump($var);
echo "</pre>";
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
if (!empty($trace)) {
$caller = $trace[0];
echo "<small>Called from: {$caller['file']}:{$caller['line']}</small>";
}
echo "</div>";
}
/**
* 生成调试报告
*/
public static function report() {
if (!self::$enabled) return;
$totalTime = microtime(true) - self::$startTime;
$peakMemory = memory_get_peak_usage();
echo "<div style='background: #e8f5e8; border: 1px solid #4caf50; padding: 15px; margin: 10px 0; font-family: Arial, sans-serif;'>";
echo "<h3 style='margin: 0 0 10px 0; color: #2e7d32;'>调试报告</h3>";
// 总体统计
echo "<p><strong>总执行时间:</strong> " . number_format($totalTime * 1000, 2) . " ms</p>";
echo "<p><strong>峰值内存使用:</strong> " . self::formatBytes($peakMemory) . "</p>";
echo "<p><strong>SQL查询数量:</strong> " . count(self::$queries) . "</p>";
// 检查点详情
if (!empty(self::$checkpoints)) {
echo "<h4>执行检查点:</h4>";
echo "<table style='width: 100%; border-collapse: collapse;'>";
echo "<tr style='background: #c8e6c9;'><th>标签</th><th>耗时(ms)</th><th>累计时间(ms)</th><th>内存使用</th></tr>";
$lastTime = self::$startTime;
foreach (self::$checkpoints as $checkpoint) {
$stepTime = ($checkpoint['time'] - $lastTime) * 1000;
$totalTime = $checkpoint['elapsed'] * 1000;
echo "<tr>";
echo "<td>{$checkpoint['label']}</td>";
echo "<td>" . number_format($stepTime, 2) . "</td>";
echo "<td>" . number_format($totalTime, 2) . "</td>";
echo "<td>" . self::formatBytes($checkpoint['memory']) . "</td>";
echo "</tr>";
$lastTime = $checkpoint['time'];
}
echo "</table>";
}
// SQL查询详情
if (!empty(self::$queries)) {
echo "<h4>SQL查询:</h4>";
foreach (self::$queries as $i => $query) {
echo "<div style='background: #fff; border: 1px solid #ddd; padding: 8px; margin: 5px 0;'>";
echo "<strong>查询 #" . ($i + 1) . ":</strong> " . htmlspecialchars($query['sql']) . "<br>";
if (!empty($query['params'])) {
echo "<strong>参数:</strong> " . htmlspecialchars(json_encode($query['params'])) . "<br>";
}
echo "<strong>执行时间:</strong> " . number_format($query['execution_time'] * 1000, 2) . " ms";
echo "</div>";
}
}
echo "</div>";
}
/**
* 格式化字节数
*/
private static function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, 2) . ' ' . $units[$pow];
}
}
// 性能分析器
class Profiler {
private static $timers = [];
private static $counters = [];
/**
* 开始计时
*/
public static function start($name) {
self::$timers[$name] = microtime(true);
}
/**
* 结束计时
*/
public static function end($name) {
if (!isset(self::$timers[$name])) {
return 0;
}
$elapsed = microtime(true) - self::$timers[$name];
unset(self::$timers[$name]);
return $elapsed;
}
/**
* 计数器
*/
public static function increment($name, $value = 1) {
if (!isset(self::$counters[$name])) {
self::$counters[$name] = 0;
}
self::$counters[$name] += $value;
}
/**
* 获取计数器值
*/
public static function getCounter($name) {
return isset(self::$counters[$name]) ? self::$counters[$name] : 0;
}
/**
* 重置所有计数器
*/
public static function reset() {
self::$timers = [];
self::$counters = [];
}
/**
* 获取所有计数器
*/
public static function getCounters() {
return self::$counters;
}
}
// 使用调试工具
echo "=== 调试工具演示 ===\n";
// 开始调试
Debugger::start();
Debugger::checkpoint('开始执行');
// 模拟一些操作
Profiler::start('database_operation');
usleep(10000); // 模拟数据库操作
Debugger::logQuery('SELECT * FROM users WHERE id = ?', [123], 0.01);
Profiler::increment('db_queries');
$dbTime = Profiler::end('database_operation');
Debugger::checkpoint('数据库操作完成');
// 模拟更多操作
Profiler::start('file_operation');
$data = ['test' => 'data', 'numbers' => range(1, 1000)];
Debugger::dump($data, '测试数据');
usleep(5000); // 模拟文件操作
Profiler::increment('file_operations');
$fileTime = Profiler::end('file_operation');
Debugger::checkpoint('文件操作完成');
// 输出性能统计
echo "数据库操作耗时:" . number_format($dbTime * 1000, 2) . " ms\n";
echo "文件操作耗时:" . number_format($fileTime * 1000, 2) . " ms\n";
echo "数据库查询次数:" . Profiler::getCounter('db_queries') . "\n";
echo "文件操作次数:" . Profiler::getCounter('file_operations') . "\n";
// 生成调试报告
Debugger::report();
?>
8.3.3 Xdebug调试工具
<?php
// Xdebug调试演示
echo "=== Xdebug调试工具 ===\n";
// 检查Xdebug是否安装
if (extension_loaded('xdebug')) {
echo "Xdebug已安装,版本:" . phpversion('xdebug') . "\n";
// 显示Xdebug配置
echo "\n--- Xdebug配置 ---\n";
$xdebugSettings = [
'xdebug.mode',
'xdebug.start_with_request',
'xdebug.client_host',
'xdebug.client_port',
'xdebug.log',
'xdebug.var_display_max_depth',
'xdebug.var_display_max_children',
'xdebug.var_display_max_data'
];
foreach ($xdebugSettings as $setting) {
$value = ini_get($setting);
echo "$setting: " . ($value !== false ? $value : '未设置') . "\n";
}
// 函数调用追踪
echo "\n--- 函数调用追踪 ---\n";
function traceDemo1() {
traceDemo2();
}
function traceDemo2() {
traceDemo3();
}
function traceDemo3() {
// 使用xdebug_get_function_stack()获取调用栈
if (function_exists('xdebug_get_function_stack')) {
$stack = xdebug_get_function_stack();
foreach ($stack as $frame) {
echo "调用:{$frame['function']}() 在 {$frame['file']}:{$frame['line']}\n";
}
}
}
traceDemo1();
// 变量显示
echo "\n--- 变量显示增强 ---\n";
$complexVar = [
'user' => [
'id' => 1,
'name' => '张三',
'profile' => [
'age' => 25,
'hobbies' => ['读书', '游泳', '编程']
]
],
'settings' => [
'theme' => 'dark',
'language' => 'zh-CN'
]
];
// Xdebug增强的var_dump输出
var_dump($complexVar);
} else {
echo "Xdebug未安装\n";
echo "\n安装Xdebug的步骤:\n";
echo "1. 访问 https://xdebug.org/wizard\n";
echo "2. 粘贴 phpinfo() 输出\n";
echo "3. 按照指示下载并安装对应版本\n";
echo "4. 在php.ini中添加配置\n";
echo "\n推荐的Xdebug配置:\n";
echo "[xdebug]\n";
echo "zend_extension=xdebug\n";
echo "xdebug.mode=debug,develop\n";
echo "xdebug.start_with_request=yes\n";
echo "xdebug.client_host=127.0.0.1\n";
echo "xdebug.client_port=9003\n";
echo "xdebug.var_display_max_depth=3\n";
echo "xdebug.var_display_max_children=128\n";
echo "xdebug.var_display_max_data=512\n";
}
?>
8.4 日志记录
8.4.1 PHP内置日志功能
<?php
// PHP内置日志功能
echo "=== PHP内置日志功能 ===\n";
// 1. error_log() 函数
echo "--- error_log函数演示 ---\n";
// 记录到系统日志
error_log("这是一条系统日志消息");
// 记录到指定文件
$logFile = 'app.log';
error_log("这是一条应用日志消息", 3, $logFile);
// 发送邮件日志(需要配置邮件服务器)
// error_log("严重错误发生", 1, "admin@example.com");
// 记录到远程系统
// error_log("远程日志消息", 3, "tcp://logserver:514");
// 2. syslog() 函数
echo "\n--- syslog函数演示 ---\n";
// 打开系统日志
openlog("MyApp", LOG_PID | LOG_PERROR, LOG_LOCAL0);
// 记录不同级别的日志
syslog(LOG_INFO, "应用启动");
syslog(LOG_WARNING, "这是一个警告");
syslog(LOG_ERR, "这是一个错误");
syslog(LOG_DEBUG, "调试信息");
// 关闭系统日志
closelog();
// 3. 自定义日志函数
function writeLog($level, $message, $context = []) {
$timestamp = date('Y-m-d H:i:s');
$contextStr = !empty($context) ? ' ' . json_encode($context) : '';
$logEntry = "[$timestamp] $level: $message$contextStr" . PHP_EOL;
file_put_contents('custom.log', $logEntry, FILE_APPEND | LOCK_EX);
}
// 使用自定义日志函数
writeLog('INFO', '用户登录', ['user_id' => 123, 'ip' => '192.168.1.1']);
writeLog('ERROR', '数据库连接失败', ['error' => 'Connection timeout']);
writeLog('DEBUG', '查询执行', ['sql' => 'SELECT * FROM users', 'time' => 0.05]);
echo "日志已写入文件\n";
?>
8.4.2 PSR-3日志接口实现
<?php
// PSR-3日志接口实现
echo "=== PSR-3日志接口实现 ===\n";
// PSR-3 LoggerInterface接口
interface LoggerInterface {
public function emergency($message, array $context = []);
public function alert($message, array $context = []);
public function critical($message, array $context = []);
public function error($message, array $context = []);
public function warning($message, array $context = []);
public function notice($message, array $context = []);
public function info($message, array $context = []);
public function debug($message, array $context = []);
public function log($level, $message, array $context = []);
}
// 日志级别常量
class LogLevel {
const EMERGENCY = 'emergency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
}
// 简单的日志实现
class SimpleLogger implements LoggerInterface {
private $logFile;
private $minLevel;
private $levels = [
LogLevel::EMERGENCY => 0,
LogLevel::ALERT => 1,
LogLevel::CRITICAL => 2,
LogLevel::ERROR => 3,
LogLevel::WARNING => 4,
LogLevel::NOTICE => 5,
LogLevel::INFO => 6,
LogLevel::DEBUG => 7
];
public function __construct($logFile = 'app.log', $minLevel = LogLevel::DEBUG) {
$this->logFile = $logFile;
$this->minLevel = $minLevel;
}
public function emergency($message, array $context = []) {
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert($message, array $context = []) {
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical($message, array $context = []) {
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error($message, array $context = []) {
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice($message, array $context = []) {
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info($message, array $context = []) {
$this->log(LogLevel::INFO, $message, $context);
}
public function debug($message, array $context = []) {
$this->log(LogLevel::DEBUG, $message, $context);
}
public function log($level, $message, array $context = []) {
// 检查日志级别
if ($this->levels[$level] > $this->levels[$this->minLevel]) {
return;
}
// 插值处理
$message = $this->interpolate($message, $context);
// 格式化日志条目
$logEntry = $this->formatLogEntry($level, $message, $context);
// 写入日志文件
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
/**
* 插值处理 - 替换消息中的占位符
*/
private function interpolate($message, array $context) {
$replace = [];
foreach ($context as $key => $val) {
if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
$replace['{' . $key . '}'] = $val;
}
}
return strtr($message, $replace);
}
/**
* 格式化日志条目
*/
private function formatLogEntry($level, $message, array $context) {
$timestamp = date('Y-m-d H:i:s');
$levelUpper = strtoupper($level);
$logEntry = "[$timestamp] $levelUpper: $message";
if (!empty($context)) {
$logEntry .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
}
return $logEntry . PHP_EOL;
}
}
// 多处理器日志记录器
class MultiLogger implements LoggerInterface {
private $loggers = [];
public function addLogger(LoggerInterface $logger) {
$this->loggers[] = $logger;
}
public function emergency($message, array $context = []) {
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert($message, array $context = []) {
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical($message, array $context = []) {
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error($message, array $context = []) {
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice($message, array $context = []) {
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info($message, array $context = []) {
$this->log(LogLevel::INFO, $message, $context);
}
public function debug($message, array $context = []) {
$this->log(LogLevel::DEBUG, $message, $context);
}
public function log($level, $message, array $context = []) {
foreach ($this->loggers as $logger) {
$logger->log($level, $message, $context);
}
}
}
// 使用日志记录器
echo "--- 使用PSR-3日志记录器 ---\n";
// 创建不同级别的日志记录器
$debugLogger = new SimpleLogger('debug.log', LogLevel::DEBUG);
$errorLogger = new SimpleLogger('error.log', LogLevel::ERROR);
// 创建多处理器日志记录器
$multiLogger = new MultiLogger();
$multiLogger->addLogger($debugLogger);
$multiLogger->addLogger($errorLogger);
// 记录不同级别的日志
$multiLogger->info('应用启动', ['version' => '1.0.0']);
$multiLogger->debug('调试信息', ['user_id' => 123]);
$multiLogger->warning('配置文件缺失', ['file' => 'config.ini']);
$multiLogger->error('数据库连接失败', ['host' => 'localhost', 'error' => 'Connection refused']);
$multiLogger->critical('系统内存不足', ['available' => '10MB', 'required' => '100MB']);
// 使用插值
$multiLogger->info('用户 {username} 从 {ip} 登录', [
'username' => '张三',
'ip' => '192.168.1.100',
'user_id' => 123
]);
echo "日志记录完成\n";
?>
8.4.3 高级日志功能
<?php
// 高级日志功能
echo "=== 高级日志功能 ===\n";
// 日志轮转类
class RotatingLogger implements LoggerInterface {
private $basePath;
private $maxFiles;
private $maxSize;
private $minLevel;
private $levels = [
LogLevel::EMERGENCY => 0,
LogLevel::ALERT => 1,
LogLevel::CRITICAL => 2,
LogLevel::ERROR => 3,
LogLevel::WARNING => 4,
LogLevel::NOTICE => 5,
LogLevel::INFO => 6,
LogLevel::DEBUG => 7
];
public function __construct($basePath = 'app.log', $maxFiles = 5, $maxSize = 1048576, $minLevel = LogLevel::DEBUG) {
$this->basePath = $basePath;
$this->maxFiles = $maxFiles;
$this->maxSize = $maxSize;
$this->minLevel = $minLevel;
}
public function emergency($message, array $context = []) {
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert($message, array $context = []) {
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical($message, array $context = []) {
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error($message, array $context = []) {
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice($message, array $context = []) {
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info($message, array $context = []) {
$this->log(LogLevel::INFO, $message, $context);
}
public function debug($message, array $context = []) {
$this->log(LogLevel::DEBUG, $message, $context);
}
public function log($level, $message, array $context = []) {
if ($this->levels[$level] > $this->levels[$this->minLevel]) {
return;
}
$currentLogFile = $this->getCurrentLogFile();
// 检查是否需要轮转
if (file_exists($currentLogFile) && filesize($currentLogFile) >= $this->maxSize) {
$this->rotateLogFiles();
}
// 格式化并写入日志
$logEntry = $this->formatLogEntry($level, $message, $context);
file_put_contents($currentLogFile, $logEntry, FILE_APPEND | LOCK_EX);
}
private function getCurrentLogFile() {
return $this->basePath;
}
private function rotateLogFiles() {
// 删除最旧的日志文件
$oldestFile = $this->basePath . '.' . $this->maxFiles;
if (file_exists($oldestFile)) {
unlink($oldestFile);
}
// 重命名现有日志文件
for ($i = $this->maxFiles - 1; $i >= 1; $i--) {
$oldFile = $this->basePath . '.' . $i;
$newFile = $this->basePath . '.' . ($i + 1);
if (file_exists($oldFile)) {
rename($oldFile, $newFile);
}
}
// 重命名当前日志文件
if (file_exists($this->basePath)) {
rename($this->basePath, $this->basePath . '.1');
}
}
private function formatLogEntry($level, $message, array $context) {
$timestamp = date('Y-m-d H:i:s');
$levelUpper = strtoupper($level);
$pid = getmypid();
$memory = memory_get_usage(true);
// 插值处理
$replace = [];
foreach ($context as $key => $val) {
if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
$replace['{' . $key . '}'] = $val;
}
}
$message = strtr($message, $replace);
$logEntry = "[$timestamp] [$pid] [$memory] $levelUpper: $message";
if (!empty($context)) {
$logEntry .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
}
return $logEntry . PHP_EOL;
}
}
// 结构化日志记录器
class StructuredLogger implements LoggerInterface {
private $logFile;
private $minLevel;
private $levels = [
LogLevel::EMERGENCY => 0,
LogLevel::ALERT => 1,
LogLevel::CRITICAL => 2,
LogLevel::ERROR => 3,
LogLevel::WARNING => 4,
LogLevel::NOTICE => 5,
LogLevel::INFO => 6,
LogLevel::DEBUG => 7
];
public function __construct($logFile = 'structured.log', $minLevel = LogLevel::DEBUG) {
$this->logFile = $logFile;
$this->minLevel = $minLevel;
}
public function emergency($message, array $context = []) {
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert($message, array $context = []) {
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical($message, array $context = []) {
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error($message, array $context = []) {
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice($message, array $context = []) {
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info($message, array $context = []) {
$this->log(LogLevel::INFO, $message, $context);
}
public function debug($message, array $context = []) {
$this->log(LogLevel::DEBUG, $message, $context);
}
public function log($level, $message, array $context = []) {
if ($this->levels[$level] > $this->levels[$this->minLevel]) {
return;
}
// 创建结构化日志条目
$logEntry = [
'timestamp' => date('c'), // ISO 8601格式
'level' => $level,
'message' => $message,
'context' => $context,
'extra' => [
'process_id' => getmypid(),
'memory_usage' => memory_get_usage(true),
'peak_memory' => memory_get_peak_usage(true),
'execution_time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']
]
];
// 添加请求信息(如果在Web环境中)
if (isset($_SERVER['REQUEST_METHOD'])) {
$logEntry['request'] = [
'method' => $_SERVER['REQUEST_METHOD'],
'uri' => $_SERVER['REQUEST_URI'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'ip' => $_SERVER['REMOTE_ADDR'] ?? ''
];
}
// 写入JSON格式的日志
$jsonLog = json_encode($logEntry, JSON_UNESCAPED_UNICODE) . PHP_EOL;
file_put_contents($this->logFile, $jsonLog, FILE_APPEND | LOCK_EX);
}
}
// 使用高级日志功能
echo "--- 使用轮转日志记录器 ---\n";
$rotatingLogger = new RotatingLogger('rotating.log', 3, 1024); // 1KB轮转
for ($i = 1; $i <= 10; $i++) {
$rotatingLogger->info("这是第 $i 条日志消息", ['iteration' => $i]);
$rotatingLogger->debug("调试信息 $i", ['data' => str_repeat('x', 100)]);
}
echo "--- 使用结构化日志记录器 ---\n";
$structuredLogger = new StructuredLogger('structured.log');
$structuredLogger->info('用户登录', [
'user_id' => 123,
'username' => '张三',
'login_method' => 'password'
]);
$structuredLogger->error('支付失败', [
'order_id' => 'ORD-12345',
'amount' => 99.99,
'currency' => 'CNY',
'error_code' => 'INSUFFICIENT_FUNDS'
]);
$structuredLogger->warning('API调用频率过高', [
'api_key' => 'key_123',
'endpoint' => '/api/users',
'rate_limit' => 100,
'current_count' => 95
]);
echo "高级日志记录完成\n";
?>
8.5 最佳实践
8.5.1 错误处理最佳实践
<?php
// 错误处理最佳实践
echo "=== 错误处理最佳实践 ===\n";
// 1. 使用适当的异常类型
class ValidationException extends Exception {
private $errors;
public function __construct($message, array $errors = [], $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->errors = $errors;
}
public function getErrors() {
return $this->errors;
}
}
class BusinessLogicException extends Exception {}
class ExternalServiceException extends Exception {}
// 2. 分层异常处理
class DataLayer {
public function findUser($id) {
if ($id <= 0) {
throw new InvalidArgumentException('用户ID必须大于0');
}
// 模拟数据库查询失败
if ($id === 999) {
throw new RuntimeException('数据库连接失败');
}
// 模拟用户不存在
if ($id === 404) {
return null;
}
return ['id' => $id, 'name' => '用户' . $id];
}
}
class BusinessLayer {
private $dataLayer;
public function __construct(DataLayer $dataLayer) {
$this->dataLayer = $dataLayer;
}
public function getUser($id) {
try {
$user = $this->dataLayer->findUser($id);
if ($user === null) {
throw new BusinessLogicException("用户 $id 不存在");
}
return $user;
} catch (InvalidArgumentException $e) {
// 参数验证错误,重新抛出
throw new ValidationException('参数验证失败', ['id' => $e->getMessage()]);
} catch (RuntimeException $e) {
// 数据层错误,包装后重新抛出
throw new ExternalServiceException('数据服务不可用', 503, $e);
}
}
}
class PresentationLayer {
private $businessLayer;
private $logger;
public function __construct(BusinessLayer $businessLayer, LoggerInterface $logger) {
$this->businessLayer = $businessLayer;
$this->logger = $logger;
}
public function handleUserRequest($id) {
try {
$user = $this->businessLayer->getUser($id);
$this->logger->info('用户查询成功', ['user_id' => $id]);
return [
'success' => true,
'data' => $user
];
} catch (ValidationException $e) {
$this->logger->warning('用户请求验证失败', [
'user_id' => $id,
'errors' => $e->getErrors()
]);
return [
'success' => false,
'error' => '请求参数无效',
'details' => $e->getErrors(),
'code' => 400
];
} catch (BusinessLogicException $e) {
$this->logger->info('业务逻辑异常', [
'user_id' => $id,
'message' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage(),
'code' => 404
];
} catch (ExternalServiceException $e) {
$this->logger->error('外部服务异常', [
'user_id' => $id,
'message' => $e->getMessage(),
'previous' => $e->getPrevious() ? $e->getPrevious()->getMessage() : null
]);
return [
'success' => false,
'error' => '服务暂时不可用,请稍后重试',
'code' => 503
];
} catch (Exception $e) {
// 未预期的异常
$this->logger->critical('未处理的异常', [
'user_id' => $id,
'exception' => get_class($e),
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
return [
'success' => false,
'error' => '系统内部错误',
'code' => 500
];
}
}
}
// 3. 优雅降级
class ServiceWithFallback {
private $primaryService;
private $fallbackService;
private $logger;
public function __construct($primaryService, $fallbackService, LoggerInterface $logger) {
$this->primaryService = $primaryService;
$this->fallbackService = $fallbackService;
$this->logger = $logger;
}
public function getData($id) {
try {
return $this->primaryService->getData($id);
} catch (Exception $e) {
$this->logger->warning('主服务失败,使用备用服务', [
'primary_error' => $e->getMessage(),
'id' => $id
]);
try {
return $this->fallbackService->getData($id);
} catch (Exception $fallbackError) {
$this->logger->error('备用服务也失败', [
'primary_error' => $e->getMessage(),
'fallback_error' => $fallbackError->getMessage(),
'id' => $id
]);
// 返回默认值或缓存数据
return $this->getDefaultData($id);
}
}
}
private function getDefaultData($id) {
return ['id' => $id, 'data' => '默认数据', 'source' => 'fallback'];
}
}
// 4. 重试机制
class RetryableService {
private $maxRetries;
private $retryDelay;
private $logger;
public function __construct($maxRetries = 3, $retryDelay = 1000, LoggerInterface $logger = null) {
$this->maxRetries = $maxRetries;
$this->retryDelay = $retryDelay;
$this->logger = $logger;
}
public function callExternalAPI($url) {
$attempt = 0;
while ($attempt < $this->maxRetries) {
try {
$attempt++;
// 模拟API调用
if (rand(1, 3) === 1) { // 33%成功率
return ['status' => 'success', 'data' => 'API响应数据'];
} else {
throw new RuntimeException('API调用失败');
}
} catch (Exception $e) {
if ($this->logger) {
$this->logger->warning('API调用失败', [
'attempt' => $attempt,
'max_retries' => $this->maxRetries,
'url' => $url,
'error' => $e->getMessage()
]);
}
if ($attempt >= $this->maxRetries) {
throw new ExternalServiceException(
"API调用在 {$this->maxRetries} 次重试后仍然失败",
0,
$e
);
}
// 等待后重试
usleep($this->retryDelay * 1000);
}
}
}
}
// 测试最佳实践
echo "--- 测试分层异常处理 ---\n";
$logger = new SimpleLogger('best_practices.log');
$dataLayer = new DataLayer();
$businessLayer = new BusinessLayer($dataLayer);
$presentationLayer = new PresentationLayer($businessLayer, $logger);
// 测试不同场景
$testCases = [1, -1, 404, 999];
foreach ($testCases as $id) {
echo "测试用户ID: $id\n";
$result = $presentationLayer->handleUserRequest($id);
echo "结果: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n\n";
}
echo "--- 测试重试机制 ---\n";
$retryService = new RetryableService(3, 500, $logger);
try {
$result = $retryService->callExternalAPI('https://api.example.com/data');
echo "API调用成功: " . json_encode($result) . "\n";
} catch (ExternalServiceException $e) {
echo "API调用最终失败: " . $e->getMessage() . "\n";
}
echo "最佳实践演示完成\n";
?>
8.5.2 生产环境配置
<?php
// 生产环境错误处理配置
echo "=== 生产环境配置 ===\n";
// 生产环境错误处理器
class ProductionErrorHandler {
private $logger;
private $notifier;
public function __construct(LoggerInterface $logger, $notifier = null) {
$this->logger = $logger;
$this->notifier = $notifier;
$this->setupErrorHandling();
}
private function setupErrorHandling() {
// 关闭错误显示
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
// 启用错误日志
ini_set('log_errors', 1);
ini_set('error_log', 'php_errors.log');
// 设置错误报告级别
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
// 注册处理器
set_error_handler([$this, 'handleError']);
set_exception_handler([$this, 'handleException']);
register_shutdown_function([$this, 'handleShutdown']);
}
public function handleError($errno, $errstr, $errfile, $errline) {
// 忽略被@抑制的错误
if (!(error_reporting() & $errno)) {
return false;
}
$errorInfo = [
'type' => $this->getErrorType($errno),
'message' => $errstr,
'file' => $errfile,
'line' => $errline,
'severity' => $errno
];
$this->logError($errorInfo);
// 严重错误需要通知
if ($errno & (E_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)) {
$this->notifyError($errorInfo);
}
return true;
}
public function handleException($exception) {
$errorInfo = [
'type' => 'Uncaught Exception',
'class' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString()
];
$this->logError($errorInfo);
$this->notifyError($errorInfo);
// 显示友好的错误页面
$this->showErrorPage();
}
public function handleShutdown() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
$errorInfo = [
'type' => 'Fatal Error',
'message' => $error['message'],
'file' => $error['file'],
'line' => $error['line']
];
$this->logError($errorInfo);
$this->notifyError($errorInfo);
}
}
private function logError($errorInfo) {
$this->logger->error('PHP Error: ' . $errorInfo['message'], $errorInfo);
}
private function notifyError($errorInfo) {
if ($this->notifier) {
$this->notifier->notify($errorInfo);
}
}
private function showErrorPage() {
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: text/html; charset=utf-8');
}
echo '<!DOCTYPE html>
<html>
<head>
<title>服务暂时不可用</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
.error-container { max-width: 500px; margin: 0 auto; }
h1 { color: #e74c3c; }
</style>
</head>
<body>
<div class="error-container">
<h1>服务暂时不可用</h1>
<p>我们正在努力解决这个问题,请稍后再试。</p>
<p>如果问题持续存在,请联系技术支持。</p>
</div>
</body>
</html>';
exit(1);
}
private function getErrorType($errno) {
$errorTypes = [
E_ERROR => 'Fatal Error',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Strict Standards',
E_RECOVERABLE_ERROR => 'Recoverable Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated'
];
return isset($errorTypes[$errno]) ? $errorTypes[$errno] : 'Unknown Error';
}
}
// 错误通知器
class ErrorNotifier {
private $webhookUrl;
private $emailRecipients;
public function __construct($webhookUrl = null, array $emailRecipients = []) {
$this->webhookUrl = $webhookUrl;
$this->emailRecipients = $emailRecipients;
}
public function notify($errorInfo) {
// 发送到Webhook(如Slack、钉钉等)
if ($this->webhookUrl) {
$this->sendWebhook($errorInfo);
}
// 发送邮件通知
if (!empty($this->emailRecipients)) {
$this->sendEmail($errorInfo);
}
}
private function sendWebhook($errorInfo) {
$payload = [
'text' => '🚨 生产环境错误警报',
'attachments' => [
[
'color' => 'danger',
'fields' => [
[
'title' => '错误类型',
'value' => $errorInfo['type'],
'short' => true
],
[
'title' => '错误消息',
'value' => $errorInfo['message'],
'short' => false
],
[
'title' => '文件位置',
'value' => $errorInfo['file'] . ':' . $errorInfo['line'],
'short' => true
],
[
'title' => '时间',
'value' => date('Y-m-d H:i:s'),
'short' => true
]
]
]
]
];
// 模拟发送Webhook
echo "发送Webhook通知: " . json_encode($payload, JSON_UNESCAPED_UNICODE) . "\n";
}
private function sendEmail($errorInfo) {
$subject = '[生产环境] 错误警报: ' . $errorInfo['type'];
$message = "错误详情:\n\n";
$message .= "类型: {$errorInfo['type']}\n";
$message .= "消息: {$errorInfo['message']}\n";
$message .= "文件: {$errorInfo['file']}\n";
$message .= "行号: {$errorInfo['line']}\n";
$message .= "时间: " . date('Y-m-d H:i:s') . "\n";
if (isset($errorInfo['trace'])) {
$message .= "\n调用栈:\n{$errorInfo['trace']}\n";
}
// 模拟发送邮件
foreach ($this->emailRecipients as $email) {
echo "发送邮件到 $email: $subject\n";
}
}
}
// 健康检查
class HealthChecker {
private $checks = [];
public function addCheck($name, callable $check) {
$this->checks[$name] = $check;
}
public function runChecks() {
$results = [];
$overallStatus = 'healthy';
foreach ($this->checks as $name => $check) {
try {
$startTime = microtime(true);
$result = call_user_func($check);
$duration = microtime(true) - $startTime;
$results[$name] = [
'status' => $result ? 'healthy' : 'unhealthy',
'duration' => round($duration * 1000, 2) . 'ms'
];
if (!$result) {
$overallStatus = 'unhealthy';
}
} catch (Exception $e) {
$results[$name] = [
'status' => 'error',
'error' => $e->getMessage()
];
$overallStatus = 'unhealthy';
}
}
return [
'status' => $overallStatus,
'timestamp' => date('c'),
'checks' => $results
];
}
}
// 使用生产环境配置
echo "--- 生产环境配置演示 ---\n";
$logger = new SimpleLogger('production.log');
$notifier = new ErrorNotifier(
'https://hooks.slack.com/services/xxx',
['admin@example.com', 'dev@example.com']
);
$errorHandler = new ProductionErrorHandler($logger, $notifier);
// 健康检查示例
$healthChecker = new HealthChecker();
$healthChecker->addCheck('database', function() {
// 模拟数据库连接检查
return rand(0, 1) === 1;
});
$healthChecker->addCheck('redis', function() {
// 模拟Redis连接检查
return true;
});
$healthChecker->addCheck('external_api', function() {
// 模拟外部API检查
if (rand(0, 1) === 0) {
throw new Exception('API连接超时');
}
return true;
});
$healthStatus = $healthChecker->runChecks();
echo "健康检查结果: " . json_encode($healthStatus, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
echo "生产环境配置完成\n";
?>
8.6 小结
本章详细介绍了PHP中的错误处理与异常机制,主要内容包括:
核心概念
- 错误类型:Fatal Error、Warning、Notice、Parse Error等
- 异常处理:try-catch-finally结构、异常传播、自定义异常
- 调试技巧:调试函数、Xdebug工具、性能分析
- 日志记录:PSR-3标准、结构化日志、日志轮转
最佳实践
- 分层异常处理:在不同层次处理不同类型的异常
- 优雅降级:提供备用方案和默认值
- 重试机制:对临时性错误进行重试
- 生产环境配置:关闭错误显示、启用日志记录、错误通知
实用工具
- 自定义错误处理器:统一处理各种错误和异常
- 日志记录器:支持多种格式和输出方式
- 调试工具类:性能分析、内存监控
- 健康检查:监控系统各组件状态
8.7 实践练习
创建异常处理系统
- 设计自定义异常类层次结构
- 实现统一的异常处理机制
- 添加日志记录和错误通知
开发调试工具
- 创建性能分析器
- 实现SQL查询监控
- 添加内存使用追踪
构建日志系统
- 实现PSR-3兼容的日志记录器
- 支持多种输出格式(文本、JSON、XML)
- 添加日志轮转和压缩功能
生产环境部署
- 配置错误处理和日志记录
- 设置监控和告警机制
- 实现健康检查端点
8.8 扩展阅读
下一章预告:第九章将介绍文件系统操作,包括文件读写、目录操作、文件上传、权限管理等内容。