2024 年 PHP 日本研討會

擴充例外

可以透過繼承內建的 Exception 類別來定義使用者自訂的例外類別。以下的成員和屬性顯示了在繼承自內建 Exception 類別的子類別中可以存取的內容。

範例 #1 內建 Exception 類別

<?php
class Exception implements Throwable
{
protected
$message = 'Unknown exception'; // 例外訊息
private $string; // __toString 快取
protected $code = 0; // 使用者定義的例外代碼
protected $file; // 例外發生的原始檔名
protected $line; // 例外發生的原始程式碼行數
private $trace; // 回溯追蹤
private $previous; // 巢狀例外發生時的先前例外

public function __construct($message = '', $code = 0, Throwable $previous = null);

final private function
__clone(); // 禁止複製例外

final public function getMessage(); // 例外訊息
final public function getCode(); // 例外代碼
final public function getFile(); // 原始檔名
final public function getLine(); // 程式碼行數
final public function getTrace(); // backtrace() 的陣列
final public function getPrevious(); // 先前的例外
final public function getTraceAsString(); // 格式化的追蹤字串

// 可覆寫
public function __toString(); // 用於顯示的格式化字串
?>

如果一個類別繼承了內建的 Exception 類別並重新定義了建構子,強烈建議它也要呼叫 parent::__construct() 以確保所有可用的資料都已正確指派。__toString() 方法可以被覆寫,以便在物件以字串呈現時提供自定義輸出。

注意事項:

例外無法被複製。嘗試複製一個例外將會導致嚴重的 E_ERROR 錯誤。

範例 #2 擴展 Exception 類別

<?php
/**
* Define a custom exception class
*/
class MyException extends Exception
{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Throwable $previous = null) {
// some code

// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}

// custom string representation of object
public function __toString() {
return
__CLASS__ . ": [{$this->code}]: {$this->message}\n";
}

public function
customFunction() {
echo
"A custom function for this type of exception\n";
}
}


/**
* Create a class to test the exception
*/
class TestException
{
public
$var;

const
THROW_NONE = 0;
const
THROW_CUSTOM = 1;
const
THROW_DEFAULT = 2;

function
__construct($avalue = self::THROW_NONE) {

switch (
$avalue) {
case
self::THROW_CUSTOM:
// throw custom exception
throw new MyException('1 is an invalid parameter', 5);
break;

case
self::THROW_DEFAULT:
// throw default one.
throw new Exception('2 is not allowed as a parameter', 6);
break;

default:
// No exception, object will be created.
$this->var = $avalue;
break;
}
}
}


// Example 1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (
MyException $e) { // Will be caught
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (
Exception $e) { // Skipped
echo "Caught Default Exception\n", $e;
}

// Continue execution
var_dump($o); // Null
echo "\n\n";


// Example 2
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (
MyException $e) { // Doesn't match this type
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (
Exception $e) { // Will be caught
echo "Caught Default Exception\n", $e;
}

// Continue execution
var_dump($o); // Null
echo "\n\n";


// Example 3
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (
Exception $e) { // Will be caught
echo "Default Exception caught\n", $e;
}

// Continue execution
var_dump($o); // Null
echo "\n\n";


// Example 4
try {
$o = new TestException();
} catch (
Exception $e) { // Skipped, no exception
echo "Default Exception caught\n", $e;
}

// Continue execution
var_dump($o); // TestException
echo "\n\n";
?>
新增註解

使用者貢獻的註解 6 則註解

iamhiddensomewhere at gmail dot com
14 年前
如前所述,例外連結是最近才新增的功能(而且真是天賜良機,它確實讓層級抽象化(以及相關的例外追蹤)變得更容易)。

由於 <5.3 缺少這個實用的功能,我採取了一些主動措施,並建立了一個自訂例外類別,讓我的所有例外都繼承自它

<?php

class SystemException extends Exception
{
private
$previous;

public function
__construct($message, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code);

if (!
is_null($previous))
{
$this -> previous = $previous;
}
}

public function
getPrevious()
{
return
$this -> previous;
}
}

?>

希望您覺得它有用。
Hayley Watson
5 年前
檢查其他 SPL 例外類別,如果您的預期例外是其中一個的子類別,則擴展其中一個。這允許在捕捉時更加精細。
michaelrfairhurst at gmail dot com
12 年前
自訂例外類別可讓您撰寫測試來證明您的例外
是有意義的。通常測試例外時,您會斷言訊息等於
某些內容,在這種情況下,您無法在不重構程式碼的情況下更改訊息格式,
或者根本不做任何斷言,在這種情況下,您可能會在之後收到誤導性訊息。
尤其是當您的 $e->getMessage 是類似 var_dump 輸出的上下文陣列之類的複雜內容時。


解決方案是將錯誤資訊從 Exception 類別抽象化到屬性中,
這樣就可以在除了一個用於測試格式的測試之外的所有測試中進行測試。

<?php

class TestableException extends Exception {

private
$property;

function
__construct($property) {

$this->property = $property;
parent::__construct($this->format($property));

}

function
format($property) {
return
"我已格式化: " . $property . "!!";
}

function
getProperty() {
return
$this->property;
}

}

function
testSomethingThrowsTestableException() {
try {
throw new
TestableException('Property');
} Catch (
TestableException $e) {
$this->assertEquals('Property', $e->getProperty());
}
}

function
testExceptionFormattingOnlyOnce() {
$e = new TestableException;
$this->assertEquals("我已格式化: 僅供必要測試使用!!",
$e->format('僅供必要測試使用')
);
}

?>
sapphirepaw.org
15 年前
PHP 5.3.0 版新增了例外連結的支援。在舊版 PHP 中,所有內建例外皆不提供 getPrevious() 方法和建構子的 $previous 參數。
Dor
13 年前
需要注意的是,Exception 類別的子類別會被預設的 Exception 處理器捕捉。

<?php

/**
* 新例外 (NewException)
* 擴展 Exception 類別,使 $message 參數變為必填。
*
*/
class NewException extends Exception {
//$message 現在不是可選的,僅用於擴展。
public function __construct($message, $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
}
}

/**
* 測試例外 (TestException)
* 測試並拋出例外。
*/
class TestException {
const
NONE = 0;
const
NORMAL = 1;
const
CUSTOM = 2;
public function
__construct($type = self::NONE) {
switch (
$type) {
case
1:
throw new
Exception('一般例外');
break;
case
2:
throw new
NewException('自訂例外');
break;
default:
return
0; //沒有拋出例外。
}
}
}

try {
$t = new TestException(TestException::CUSTOM);
}
catch (
Exception $e) {
print_r($e); //例外被捕捉
}

?>

請注意,如果一個例外被捕捉一次,它將不會再次被捕捉(即使是更具體的處理程序)。
shaman_master at list dot ru
9 年前
使用此範例作為非數字代碼
<code>
<?php
class MyException extends Exception
{
/**
* 建立新的例外。
*
* @param string $message 錯誤訊息
* @param mixed $code 例外代碼
* @param Exception $previous 先前的例外
* @return void
*/
public function __construct($message = '', $code = 0, Exception $previous = null)
{
// 將訊息和整數代碼傳遞給父類別
parent::__construct((string)$message, (int)$code, $previous);

// @link http://bugs.php.net/39615 儲存未修改的代碼
$this->code = $code;
}
}
</
code>
To Top