異常

目錄

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

如果拋出異常的函數(shù)范圍內(nèi)沒有 catch 塊,異常會沿調(diào)用?!跋蛏厦芭荨?, 直到找到匹配的 catch 塊。 沿途會執(zhí)行所有遇到的 finally 塊。 在沒有設(shè)置全局異常處理程序(exception handler)時, 如果調(diào)用棧向上都沒有遇到匹配的 catch,程序會拋出 fatal 錯誤并終止執(zhí)行。

拋出的對象必須是 Exception 自身或 Exception的子類。 拋出其他對象會導(dǎo)致 PHP 報 Fatal 錯誤。

PHP 8.0.0 起,throw 關(guān)鍵詞現(xiàn)在開始是一個表達(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 之后,或者直接代替它。 無論是否拋出了異常,在 trycatch 之后、在執(zhí)行后續(xù)代碼之前, 放在 finally 里的代碼總是會執(zhí)行。

值得注意的是 finallyreturn 語句之間存在相互影響。 如果在 trycatch 里遇到 return,仍然會執(zhí)行 finally 里的代碼。 而且,遇到 return 語句時,會先執(zhí)行 finally 再返回結(jié)果。 此外,如果 finally 里也包含了 return 語句,將返回 finally 里的值。

全局異常處理器

當(dāng)允許異常冒泡到全局范圍時,它可以被全局異常處理器捕獲到。 set_exception_handler() 可以設(shè)置一個函數(shù),在沒有調(diào)用其他塊時代替 catch。 在本質(zhì)上,實現(xiàn)的效果等同于整個程序被 try-catch 包裹起來, 而該函數(shù)就是 catch

注釋

注意:

PHP 內(nèi)部函數(shù)主要使用 錯誤報告, 只有一些現(xiàn)代 面向?qū)ο?/a> 的擴(kuò)展使用異常。 不過,錯誤很容易用 ErrorException 轉(zhuǎn)化成異常。 然而,這個技術(shù)方案僅適用非 Fatal 級的錯誤。

示例 #3 將錯誤報告轉(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 拋出一個異常

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

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