首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 錯誤與異常處理完整指南:從 die() 到現代異常體系

     編輯:PHP 相關知識

錯誤處理演進層級

  1. die()/exit():最原始,停止執行,輸出信息,無法捕獲、無法恢復。
  2. trigger_error() + set_error_handler():自定義錯誤處理器,可轉為異常、記錄日誌、發送郵件。
  3. try/catch + throw Exception:現代面向對象異常體系,支援類型捕獲、堆疊追蹤、重新拋出。
  4. Throwable 接口 (PHP 7+):統一 Error 和 Exception,\Throwable 可捕獲所有可拋出對象。

錯誤級別常量速查

常量值描述可被 set_error_handler 捕獲
E_ERROR1致命運行時錯誤(內存耗盡)否
E_WARNING2運行時警告(非致命)是
E_PARSE4編譯時語法錯誤否
E_NOTICE8運行時通知(如未定義變量)是
E_DEPRECATED8192棄用特性警告是
E_USER_ERROR256用戶觸發致命錯誤否
E_USER_WARNING512用戶觸發警告是
E_USER_NOTICE1024用戶觸發通知是
E_RECOVERABLE_ERROR4096可捕獲致命錯誤(類型提示失敗)是
E_ALL32767除 E_STRICT 外所有-

自定義錯誤處理器最佳實踐

<?php
// bootstrap.php 或入口文件最早引入
function appErrorHandler(int $level, string $message, string $file, int $line, array $context = []): bool {
    // 1. 過濾 error_reporting 不包含的級別
    if (!(error_reporting() & $level)) return false; // 交給 PHP 內置處理

    // 2. 忽略 @ 抑制的錯誤
    if (error_reporting() === 0) return false;

    // 3. 構建統一日誌格式
    $log = [
        'time'    => date('c'),
        'level'   => errorLevelToString($level),
        'message' => $message,
        'file'    => $file,
        'line'    => $line,
        'trace'   => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5),
    ];

    // 4. 記錄日誌(Monolog/文件/系統日誌)
    error_log(json_encode($log, JSON_UNESCAPED_UNICODE), 3, __DIR__ . '/logs/php_errors.log');

    // 5. 生產環境:致命錯誤發送告警(郵件/Slack/釘釘)
    if (in_array($level, [E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR], true)) {
        // 異步發送告警...
    }

    // 6. 返回 true 表示已處理,阻止 PHP 內置輸出
    return true;
}

set_error_handler('appErrorHandler');

// 將錯誤轉為異常(便於 try/catch 統一處理)
function errorToException(int $level, string $message, string $file, int $line): void {
    if (!(error_reporting() & $level)) return;
    throw new ErrorException($message, 0, $level, $file, $line);
}
set_error_handler('errorToException', E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);

異常處理現代模式

<?php
// 1. 定義業務異常層級
namespace App\Exception;

class BusinessException extends \Exception {
    public function __construct(string $message, int $code = 400, \Throwable $previous = null) {
        parent::__construct($message, $code, $previous);
    }
}

class NotFoundException extends BusinessException {
    public function __construct(string $message = '資源不存在', \Throwable $previous = null) {
        parent::__construct($message, 404, $previous);
    }
}

class ValidationException extends BusinessException {
    public array $errors;
    public function __construct(array $errors, \Throwable $previous = null) {
        $this->errors = $errors;
        parent::__construct('驗證失敗', 422, $previous);
    }
}

// 2. 業務代碼拋出
function transfer(int $from, int $to, float $amount): void {
    if ($amount <= 0) throw new ValidationException(['amount' => '金額必須大於 0']);
    $fromUser = User::find($from);
    if (!$fromUser) throw new NotFoundException("轉出用戶 {$from} 不存在");
    if ($fromUser->balance < $amount) throw new BusinessException('餘額不足', 400);
    // ... 執行轉賬
}

// 3. 統一異常處理入口(框架通常提供)
try {
    // 路由分發、控制器執行
    $response = $app->handle($request);
} catch (ValidationException $e) {
    $response = jsonResponse(['code' => $e->getCode(), 'msg' => $e->getMessage(), 'errors' => $e->errors], $e->getCode());
} catch (NotFoundException $e) {
    $response = jsonResponse(['code' => 404, 'msg' => $e->getMessage()], 404);
} catch (BusinessException $e) {
    $response = jsonResponse(['code' => $e->getCode(), 'msg' => $e->getMessage()], $e->getCode());
} catch (\Throwable $e) {
    // 記錄完整堆疊
    Log::critical($e->getMessage(), ['trace' => $e->getTraceAsString()]);
    // 生產環境不暴露內部細節
    $response = jsonResponse(['code' => 500, 'msg' => '服務器內部錯誤'], 500);
}

// 4. 註冊 shutdown 捕獲致命錯誤
register_shutdown_function(function () {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
        // 記錄致命錯誤
        Log::emergency('Fatal Error', $error);
        // 可選:輸出友好頁面
        if (php_sapi_name() !== 'cli') {
            http_response_code(500);
            echo '系統繁忙,請稍後重試';
        }
    }
});

日誌記錄最佳實踐

  • 使用 Monolog 實現 PSR-3 標準,支援多通道(文件、Syslog、Slack、Elasticsearch)。
  • 日誌級別:debug < info < notice < warning < error < critical < alert < emergency。
  • 結構化日誌(JSON),包含:request_id、user_id、trace_id、span_id,便於鏈路追蹤。
  • 敏感數據脫敏(密碼、Token、身份證、銀行卡)。
  • 日誌輪轉:按天/大小切割,保留 30 天,壓縮歸檔。

生產環境配置清單

; php.ini
display_errors = Off          ; 絕不向用戶顯示錯誤
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/error.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
; 生產環境建議關閉嚴格標準提示,減少噪音
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved