| 場景 | finally 執行 | 返回值 |
try 正常 return | ✅ 是 | 若 finally 有 return,以 finally 為準 |
catch return | ✅ 是 | 同上 |
| try/throw 未捕獲 | ✅ 是 | 異常向上拋出,finally 仍執行 |
die()/exit() | ❌ 否 | 腳本直接終止 |
| 致命錯誤 | ❌ 否 | 引擎崩潰 |
返回值覆蓋陷阱演示
function testReturn() {
try {
return 'try return';
} finally {
return 'finally return'; // 覆蓋 try 的返回值
}
}
echo testReturn(); // 輸出:finally return
function testReturn2() {
$val = 'try';
try {
return $val; // 返回值已確定為 'try'
} finally {
$val = 'finally'; // 修改變量不影響已確定的返回值
}
}
echo testReturn2(); // 輸出:try(PHP 7.0+ 行為)
// 表達式求值差異
function testReturn3() {
$val = 1;
try {
return $val + 0; // 表達式求值,返回值為 1
} finally {
$val++;
}
}
echo testReturn3(); // 輸出:1(PHP 7.0+ 一致行為)
異常鏈:previous 異常保留原始堆疊
try {
try {
throw new Exception('原始錯誤:數據庫連接失敗');
} catch (Exception $e) {
// 包裝為業務異常,保留原始異常
throw new ServiceException('用戶服務不可用', 0, $e);
}
} catch (ServiceException $e) {
echo "業務異常:{$e->getMessage()}\n";
if ($e->getPrevious()) {
echo "根因:{$e->getPrevious()->getMessage()}\n";
echo "根因堆疊:\n{$e->getPrevious()->getTraceAsString()}\n";
}
}
命名空間下捕獲異常:必須轉義或導入
namespace App\Services;
use Exception; // 導入全局 Exception
// 或 use Throwable; // PHP 7+ 基礎介面
class PaymentService {
public function charge() {
try {
// ...
} catch (\Exception $e) { // 寫法 1:前導 \ 表示全局
// 處理
} catch (Exception $e) { // 寫法 2:配合 use Exception
// 處理
}
// 錯誤寫法:catch (Exception $e) 無 use 會嘗試捕獲 App\Services\Exception
}
}
Throwable 體系(PHP 7+)
Throwable (interface)
├── Error (引擎錯誤:TypeError, ParseError, ArithmeticError, AssertionError)
└── Exception (用戶代碼異常)
├── LogicException (邏輯錯誤:BadFunctionCall, DomainException, InvalidArgument, Length, OutOfRange)
└── RuntimeException (運行時錯誤:OutOfBounds, Overflow, Range, Underflow, UnexpectedValue)
最佳實踐:catch (Throwable $e) 可同時捕獲 Error 和 Exception,適合頂層兜底處理器。
自定義異常體系設計
abstract class AppException extends Exception {
protected $errorCode; // 業務錯誤碼
public function __construct(string $message = '', int $code = 0, int $errorCode = 0, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->errorCode = $errorCode;
}
public function getErrorCode(): int { return $this->errorCode; }
}
class ValidationException extends AppException {} // 400
class AuthenticationException extends AppException {} // 401
class AuthorizationException extends AppException {} // 403
class NotFoundException extends AppException {} // 404
class ServiceException extends AppException {} // 500
全局異常處理器(生產環境)
set_exception_handler(function (Throwable $e) {
// 1. 記錄完整日誌
error_log(sprintf(
"[%s] %s in %s:%d\nStack:\n%s",
date('c'), $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString()
));
// 2. 區分環境輸出
if (ini_get('display_errors')) {
// 開發環境:顯示詳情
echo "Uncaught Exception
" . htmlspecialchars($e->getMessage()) . "
";
} else {
// 生產環境:友好提示
http_response_code(500);
echo "系統繁忙,請稍後再試。";
}
});
// 可選:註冊致命錯誤處理
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
// 交給同一處理器
set_exception_handler(function (Throwable $e) { /* ... */ });
// 構造 ErrorException
throw new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
}
});
結語
PHP 7+ 異常機制已趨成熟:finally 保證資源釋放、Throwable 統一捕獲、previous 保留異常鏈、命名空間轉義避免誤捕。建立分層異常體系、配置全局處理器、區分開發/生產輸出,是構建健壯應用的基石。記住:finally 中的 return 會覆蓋外層返回值,謹慎使用。
- 上一篇文章: 微博粉絲服務平台開發完整指南:從微信遷移、接口驗證到消息收發實戰
- 下一篇文章: PHP 過濾 ASCII 控制字符(精簡版):strtr 靜態映射表極速清洗
Copyright ©
程式碼之家 All Rights Reserved