PHP 5.3 引入兩大特性:嵌套異常(getPrevious())與 SPL 內建異常類型(13 種),配合組件級 Marker Interface,形成現代異常處理最佳實踐。
場景:適配器模式封裝第三方庫/擴展(PDO、cURL、Soap),將其異常轉為統一的組件異常,同時保留原始異常。
class MyDb {
private $pdo;
public function execute(string $sql): int {
try {
return $this->pdo->exec($sql);
} catch (PDOException $e) {
throw new MyDbException('Query failed', 0, $e); // 第三參數為 previous
}
}
}
// 呼叫端
try {
$db->execute($sql);
} catch (MyDbException $e) {
echo "業務錯誤:{$e->getMessage()}\n";
if ($prev = $e->getPrevious()) {
echo "底層錯誤:{$prev->getMessage()} (Code: {$prev->getCode()})\n";
}
}
| 分類 | 異常類別 | 繼承鏈 | 適用場景 |
|---|---|---|---|
| 基類 | LogicException | Exception | 邏輯錯誤(預期可避免) |
| RuntimeException | Exception | 運行時錯誤(外部因素) | |
| 動態調用 | BadFunctionCallException | LogicException | is_callable() 失敗、__call() 不存在 |
| BadMethodCallException | BadFunctionCallException | 方法不存在/不可呼叫 | |
| 邏輯組 | DomainException | LogicException | 領域規則違反(如數值超出定義域) |
| InvalidArgumentException | LogicException | 參數型別/值不合法 | |
| LengthException | LogicException | 長度不符(陣列/字串) | |
| OutOfRangeException | LogicException | 索引超出合法範圍 | |
| 運行時組 | OutOfBoundsException | RuntimeException | 運行時索引越界(如陣列操作) |
| OverflowException | RuntimeException | 容器溢位(如堆疊滿) | |
| RangeException | RuntimeException | 運行時數值超出範圍 | |
| UnderflowException | RuntimeException | 容器下溢(如堆疊空) | |
| UnexpectedValueException | RuntimeException | 運行時遇到非預期值 |
LogicException 子類。RuntimeException 子類。目的:讓上層可針對「某組件的所有異常」捕獲,同時保留具體 SPL 類型語義。
namespace MyLib\Component;
interface Exception {} // Marker Interface
class InvalidArgumentException extends \InvalidArgumentException implements Exception {}
class RuntimeException extends \RuntimeException implements Exception {}
// 組件內部
class Processor {
public function run($input) {
if (!is_string($input)) {
throw new InvalidArgumentException('Input must be string');
}
if ($this->externalServiceFails()) {
throw new RuntimeException('Service unavailable');
}
}
}
try {
$proc->run($data);
} catch (MyLib\Component\InvalidArgumentException $e) {
// 針對性處理
} catch (MyLib\Component\Exception $e) {
// 組件通用處理
} catch (\InvalidArgumentException $e) {
// SPL 類型處理
} catch (\Exception $e) {
// 最後防線
}
Exception。$previous。Exception,所有內部異常實作它。善用嵌套異常保留因果鏈、SPL 類型語義化錯誤分類、Marker Interface 實現組件級捕獲,三者結合構建清晰、可維護、可擴展的異常處理體系。