PHP 有一個(gè)和其他語(yǔ)言相似的異常模型。
在 PHP 里可以 throw
并 catch
異常。
為了捕獲潛在的異常,可以將代碼包含在 try
塊里。
每個(gè) try
都必須有一個(gè)相應(yīng)的
catch
或 finally
代碼塊。
如果拋出異常的函數(shù)范圍內(nèi)沒(méi)有 catch
塊,異常會(huì)沿調(diào)用?!跋蛏厦芭荨保? 直到找到匹配的 catch
塊。
沿途會(huì)執(zhí)行所有遇到的 finally
塊。
在沒(méi)有設(shè)置全局異常處理程序(exception handler)時(shí),
如果調(diào)用棧向上都沒(méi)有遇到匹配的 catch
,程序會(huì)拋出 fatal 錯(cuò)誤并終止執(zhí)行。
拋出的對(duì)象必須是 Exception 自身或 Exception的子類(lèi)。 拋出其他對(duì)象會(huì)導(dǎo)致 PHP 報(bào) Fatal 錯(cuò)誤。
PHP 8.0.0 起,throw
關(guān)鍵詞現(xiàn)在開(kāi)始是一個(gè)表達(dá)式,可用于任何表達(dá)式的場(chǎng)景。
在此之前,它是一個(gè)語(yǔ)句,必須獨(dú)占一行。
catch
catch
定義了處理拋出異常的方式。
catch
塊定義了它能處理的異常/錯(cuò)誤的類(lèi)型,并可以選擇將異常賦值到變量中。
(在 PHP 8.0.0 之前的版本中必須要賦值到變量)
如果遇到拋出對(duì)象的類(lèi)型匹配了首個(gè) catch
塊的異常或錯(cuò)誤,將會(huì)處理該對(duì)象。
可用多個(gè) catch
捕獲不同的異常類(lèi)。
正常情況下(try
代碼塊里沒(méi)有拋出異常)會(huì)在最后一個(gè)定義的 catch
后面繼續(xù)執(zhí)行。
catch
代碼塊里也可以 throw
或者重新拋出異常。
不拋出的話(huà),會(huì)在觸發(fā)的 catch
后面繼續(xù)執(zhí)行。
當(dāng) PHP 拋出一個(gè)異常時(shí),將不會(huì)執(zhí)行后續(xù)的代碼語(yǔ)句,并會(huì)嘗試查找首個(gè)匹配的 catch
代碼塊。
如果沒(méi)有用 set_exception_handler() 設(shè)置異常處理函數(shù),
PHP 會(huì)在異常未被捕獲時(shí)產(chǎn)生 Fatal 級(jí)錯(cuò)誤,提示 "Uncaught Exception ...
"
消息。
從 PHP 7.1.0 起 catch
可以用豎線(xiàn)符(|
) 指定多個(gè)異常。
如果在不同的類(lèi)層次結(jié)構(gòu)中,不同異常的異常需要用同樣的方式處理,就特別適用這種方式。
從 PHP 8.0.0 起,捕獲的異常不再?gòu)?qiáng)制要求指定變量名。
catch
代碼塊會(huì)在未指定時(shí)繼續(xù)執(zhí)行,只是無(wú)法訪(fǎng)問(wèn)到拋出的對(duì)象。
finally
finally
代碼塊可以放在 catch
之后,或者直接代替它。
無(wú)論是否拋出了異常,在 try
和 catch
之后、在執(zhí)行后續(xù)代碼之前,
放在 finally
里的代碼總是會(huì)執(zhí)行。
值得注意的是 finally
和 return
語(yǔ)句之間存在相互影響。
如果在 try
或 catch
里遇到 return
,仍然會(huì)執(zhí)行 finally
里的代碼。
而且,遇到 return
語(yǔ)句時(shí),會(huì)先執(zhí)行 finally
再返回結(jié)果。
此外,如果 finally
里也包含了 return
語(yǔ)句,將返回 finally
里的值。
全局異常處理器
當(dāng)允許異常冒泡到全局范圍時(shí),它可以被全局異常處理器捕獲到。
set_exception_handler()
可以設(shè)置一個(gè)函數(shù),在沒(méi)有調(diào)用其他塊時(shí)代替 catch
。
在本質(zhì)上,實(shí)現(xiàn)的效果等同于整個(gè)程序被 try
-catch
包裹起來(lái),
而該函數(shù)就是 catch
。
注意:
PHP 內(nèi)部函數(shù)主要使用 錯(cuò)誤報(bào)告, 只有一些現(xiàn)代 面向?qū)ο?/a> 的擴(kuò)展使用異常。 不過(guò),錯(cuò)誤很容易用 ErrorException 轉(zhuǎn)化成異常。 然而,這個(gè)技術(shù)方案僅適用非 Fatal 級(jí)的錯(cuò)誤。
示例 #3 將錯(cuò)誤報(bào)告轉(zhuǎn)成異常
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
PHP 標(biāo)準(zhǔn)庫(kù)(SPL) 提供了大量的 標(biāo)準(zhǔn)內(nèi)置異常。
示例 #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";
}
// 繼續(xù)執(zhí)行
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";
}
// 繼續(xù)執(zhí)行
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 以表達(dá)式的形式拋出
僅僅在 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();
}
?>