错误处理是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 实践练习

  1. 创建异常处理系统

    • 设计自定义异常类层次结构
    • 实现统一的异常处理机制
    • 添加日志记录和错误通知
  2. 开发调试工具

    • 创建性能分析器
    • 实现SQL查询监控
    • 添加内存使用追踪
  3. 构建日志系统

    • 实现PSR-3兼容的日志记录器
    • 支持多种输出格式(文本、JSON、XML)
    • 添加日志轮转和压缩功能
  4. 生产环境部署

    • 配置错误处理和日志记录
    • 设置监控和告警机制
    • 实现健康检查端点

8.8 扩展阅读


下一章预告:第九章将介绍文件系统操作,包括文件读写、目录操作、文件上传、权限管理等内容。