PHP 有一個(gè)和其他語(yǔ)言相似的異常模型。
在 PHP 里可以 throw
并 catch
異常。
為了捕獲潛在的異常,可以將代碼包含在 try
塊里。
每個(gè) try
都必須有一個(gè)相應的
catch
或 finally
代碼塊。
如果拋出異常的函數范圍內沒(méi)有 catch
塊,異常會(huì )沿調用?!跋蛏厦芭荨?,
直到找到匹配的 catch
塊。
沿途會(huì )執行所有遇到的 finally
塊。
在沒(méi)有設置全局異常處理程序(exception handler)時(shí),
如果調用棧向上都沒(méi)有遇到匹配的 catch
,程序會(huì )拋出 fatal 錯誤并終止執行。
拋出的對象必須是 Exception 自身或 Exception的子類(lèi)。 拋出其他對象會(huì )導致 PHP 報 Fatal 錯誤。
PHP 8.0.0 起,throw
關(guān)鍵詞現在開(kāi)始是一個(gè)表達式,可用于任何表達式的場(chǎng)景。
在此之前,它是一個(gè)語(yǔ)句,必須獨占一行。
catch
catch
定義了處理拋出異常的方式。
catch
塊定義了它能處理的異常/錯誤的類(lèi)型,并可以選擇將異常賦值到變量中。
(在 PHP 8.0.0 之前的版本中必須要賦值到變量)
如果遇到拋出對象的類(lèi)型匹配了首個(gè) catch
塊的異?;蝈e誤,將會(huì )處理該對象。
可用多個(gè) catch
捕獲不同的異常類(lèi)。
正常情況下(try
代碼塊里沒(méi)有拋出異常)會(huì )在最后一個(gè)定義的 catch
后面繼續執行。
catch
代碼塊里也可以 throw
或者重新拋出異常。
不拋出的話(huà),會(huì )在觸發(fā)的 catch
后面繼續執行。
當 PHP 拋出一個(gè)異常時(shí),將不會(huì )執行后續的代碼語(yǔ)句,并會(huì )嘗試查找首個(gè)匹配的 catch
代碼塊。
如果沒(méi)有用 set_exception_handler() 設置異常處理函數,
PHP 會(huì )在異常未被捕獲時(shí)產(chǎn)生 Fatal 級錯誤,提示 "Uncaught Exception ...
"
消息。
從 PHP 7.1.0 起 catch
可以用豎線(xiàn)符(|
) 指定多個(gè)異常。
如果在不同的類(lèi)層次結構中,不同異常的異常需要用同樣的方式處理,就特別適用這種方式。
從 PHP 8.0.0 起,捕獲的異常不再強制要求指定變量名。
catch
代碼塊會(huì )在未指定時(shí)繼續執行,只是無(wú)法訪(fǎng)問(wèn)到拋出的對象。
finally
finally
代碼塊可以放在 catch
之后,或者直接代替它。
無(wú)論是否拋出了異常,在 try
和 catch
之后、在執行后續代碼之前,
放在 finally
里的代碼總是會(huì )執行。
值得注意的是 finally
和 return
語(yǔ)句之間存在相互影響。
如果在 try
或 catch
里遇到 return
,仍然會(huì )執行 finally
里的代碼。
而且,遇到 return
語(yǔ)句時(shí),會(huì )先執行 finally
再返回結果。
此外,如果 finally
里也包含了 return
語(yǔ)句,將返回 finally
里的值。
全局異常處理器
當允許異常冒泡到全局范圍時(shí),它可以被全局異常處理器捕獲到。
set_exception_handler()
可以設置一個(gè)函數,在沒(méi)有調用其他塊時(shí)代替 catch
。
在本質(zhì)上,實(shí)現的效果等同于整個(gè)程序被 try
-catch
包裹起來(lái),
而該函數就是 catch
。
注意:
PHP 內部函數主要使用 錯誤報告, 只有一些現代 面向對象 的擴展使用異常。 不過(guò),錯誤很容易用 ErrorException 轉化成異常。 然而,這個(gè)技術(shù)方案僅適用非 Fatal 級的錯誤。
示例 #3 將錯誤報告轉成異常
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
PHP 標準庫(SPL) 提供了大量的 標準內置異常。
示例 #4 拋出一個(gè)異常
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// 繼續執行
echo "Hello World\n";
?>
以上例程會(huì )輸出:
0.2 Caught exception: Division by zero. Hello World
示例 #5 帶 finally
塊的異常處理
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "First finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "Second finally.\n";
}
// 繼續執行
echo "Hello World\n";
?>
以上例程會(huì )輸出:
0.2 First finally. Caught exception: Division by zero. Second finally. Hello World
示例 #6 finally
和 return
相互之間的影響
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
以上例程會(huì )輸出:
finally
示例 #7 異常嵌套
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// 重新 throw
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
以上例程會(huì )輸出:
string(4) "foo!"
示例 #8 多個(gè)異常的捕獲處理
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>
以上例程會(huì )輸出:
string(11) "MyException"
示例 #9 忽略捕獲的變量
僅僅在 PHP 8.0.0 及以上版本有效
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print "A SpecificException was thrown, but we don't care about the details.";
}
?>
示例 #10 以表達式的形式拋出
僅僅在 PHP 8.0.0 及以上版本有效
<?php
class SpecificException extends Exception {}
function test() {
do_something_risky() or throw new Exception('It did not work');
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>