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