PHP 5 引入 OO 異常機制,取代傳統錯誤代碼/返回值檢查。本文系統講解 try-catch-finally、自定義異常類別、多重捕獲順序、重新拋出、set_exception_handler() 等核心技術,附執行流程圖解。
<?php
try {
// 可能拋出異常的代碼
$result = riskyOperation();
} catch (SpecificException $e) {
// 捕獲特定異常
handleSpecific($e);
} catch (Exception $e) {
// 捕獲其他所有異常(父類型放最後)
handleGeneric($e);
} finally {
// PHP 5.5+,無論是否異常都執行(清理資源)
cleanup();
}
?>
<?php
// 業務異常基類
class AppException extends Exception {}
// 具體業務異常
class HeavyParcelException extends AppException {}
class InvalidAddressException extends AppException {}
class PaymentFailedException extends AppException {}
// 可攜帶額外資料
class ApiException extends Exception {
public function __construct(string $message, int $code, public array $context = []) {
parent::__construct($message, $code);
}
}
?>
<?php
class Parcel {
public string $address = '';
public float $weight = 0;
}
class Courier {
public function ship(Parcel $parcel): bool {
if (empty($parcel->address)) {
throw new InvalidAddressException('地址未填寫');
}
if ($parcel->weight > 5) {
throw new HeavyParcelException('包裹超過 5kg 限制');
}
return true;
}
}
$courier = new Courier();
$parcel = new Parcel();
$parcel->weight = 7; // 故意超重
try {
$courier->ship($parcel);
echo "派送成功";
} catch (HeavyParcelException $e) {
echo "重量異常: {$e->getMessage()}"; // 捕獲特定
// 導向選擇其他快遞
} catch (InvalidAddressException $e) {
echo "地址異常: {$e->getMessage()}";
exit; // 終止後續流程
} catch (Exception $e) {
echo "未知錯誤: {$e->getMessage()}";
}
echo "
程式繼續執行...";
?>
try 區塊執行 ship()。ship() 先檢查地址 → 拋出 InvalidAddressException。catch:第一個 HeavyParcelException 不匹配 → 第二個 InvalidAddressException 匹配 → 執行該區塊。exit 終止腳本,finally(若有)仍會執行。⚠️ 捕獲順序原則:子類在前,父類在後。若寫反,父類會「吞掉」子類異常。
<?php
try {
$pdo->beginTransaction();
// ...
$pdo->commit();
} catch (PDOException $e) {
$pdo->rollBack();
// 記錄日誌後重新拋出,讓上層決定如何處理
error_log($e->getMessage());
throw $e; // 或 throw new AppException('DB 錯誤', 0, $e);
}
?>
<?php
try {
// ...
} catch (Throwable $e) {
throw new RuntimeException('處理失敗', 500, $e); // $e 成為 previous
}
// 上層可用 $e->getPrevious() 追溯根因
?>
<?php
set_exception_handler(function (Throwable $e) {
// 記錄完整堆疊
error_log("未捕獲異常: {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}\n{$e->getTraceAsString()}");
// 回傳友善錯誤頁(非 CLI)
if (php_sapi_name() !== 'cli') {
http_response_code(500);
echo '系統繁忙,請稍後再試';
}
});
?>
<?php
set_error_handler(function (int $severity, string $message, string $file, int $line) {
if (!(error_reporting() & $severity)) return false; // 忽略 @ 抑制的錯誤
throw new ErrorException($message, 0, $severity, $file, $line);
});
?>
Exception 和 Error 都實作 Throwable。catch (Throwable $e) 可同時捕獲異常與致命錯誤(TypeError、ParseError 等)。
Exception 結尾,繼承 Exception 或 AppException。finally 釋放資源(關閉連線、釋放鎖、清理暫存檔)。display_errors,啟用 log_errors + 頂層處理器。異常處理的核心是關注點分離:業務邏輯專注「快樂路徑」,錯誤處理集中在 catch、頂層處理器。掌握捕獲順序、異常鏈、重新拋出,程式碼健壯度大幅提升。