異常

目錄

PHP 有一個(gè)和其他語(yǔ)言相似的異常模型。 在 PHP 里可以 throwcatch 異常。 為了捕獲潛在的異常,可以將代碼包含在 try 塊里。 每個(gè) try 都必須有一個(gè)相應(yīng)的 catchfinally 代碼塊。

如果拋出異常的函數(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ú)論是否拋出了異常,在 trycatch 之后、在執(zhí)行后續(xù)代碼之前, 放在 finally 里的代碼總是會(huì)執(zhí)行。

值得注意的是 finallyreturn 語(yǔ)句之間存在相互影響。 如果在 trycatch 里遇到 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($message0$severity$filename$lineno);
}

set_error_handler('exceptions_error_handler');
?>

范例

示例 #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 finallyreturn 相互之間的影響

<?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();
}
?>