PHP Conference Japan 2024

ErrorException

(PHP 5 >= 5.1.0,PHP 7,PHP 8)

簡介

錯誤例外。

類別綱要

class ErrorException extends Exception {
/* 屬性 */
protected int $severity = E_ERROR;
/* 繼承的屬性 */
protected string $message = "";
private string $string = "";
protected int $code;
protected string $file = "";
protected int $line;
private array $trace = [];
private ?Throwable $previous = null;
/* 方法 */
public __construct(
    string $message = "",
    int $code = 0,
    int $severity = E_ERROR,
    ?string $filename = null,
    ?int $line = null,
    ?Throwable $previous = null
)
final public getSeverity(): int
/* 繼承的方法 */
final public Exception::getCode(): int
final public Exception::getFile(): string
final public Exception::getLine(): int
final public Exception::getTrace(): array
}

屬性

severity

例外的嚴重性

範例

範例 #1 使用 set_error_handler() 將錯誤訊息轉換為 ErrorException

<?php

set_error_handler
(function (int $errno, string $errstr, string $errfile, int $errline) {
if (!(
error_reporting() & $errno)) {
// 此錯誤碼未包含在 error_reporting 中。
return;
}

if (
$errno === E_DEPRECATED || $errno === E_USER_DEPRECATED) {
// 不要為過時警告拋出例外,因為新的或未預期的
// 過時會破壞應用程式。
return;
}

throw new
\ErrorException($errstr, 0, $errno, $errfile, $errline);
});

// 反序列化損壞的資料會觸發警告,此警告會由錯誤處理常式轉換為
// ErrorException。
unserialize('broken data');

?>

上面的範例會輸出類似的內容

Fatal error: Uncaught ErrorException: unserialize(): Error at offset 0 of 11 bytes in test.php:16
Stack trace:
#0 [internal function]: {closure}(2, 'unserialize(): ...', 'test.php', 16)
#1 test.php(16): unserialize('broken data')
#2 {main}
  thrown in test.php on line 16

目錄

新增註解

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

18
randallgirard at hotmail dot com
14 年前
E_USER_WARNING、E_USER_NOTICE 和任何其他非終止錯誤碼,在將自訂 ERROR_HANDLER 與 ErrorException 結合且未捕捉錯誤時,會變得無用,且像 E_USER_ERROR(會終止)一樣運作。在 EXCEPTION_HANDLER 中,沒有任何方法可以將執行返回父範圍。

<?php

error_reporting
(E_ALL);
define('DEBUG', true);
define('LINEBREAK', "\r\n");

error::initiate('./error_backtrace.log');

try
trigger_error("First error", E_USER_NOTICE);
catch (
ErrorException $e )
print(
"Caught the error: ".$e->getMessage."<br />\r\n" );

trigger_error("This event WILL fire", E_USER_NOTICE);

trigger_error("This event will NOT fire", E_USER_NOTICE);

abstract class
error {

public static
$LIST = array();

private function
__construct() {}

public static function
initiate( $log = false ) {
set_error_handler( 'error::err_handler' );
set_exception_handler( 'error::exc_handler' );
if (
$log !== false ) {
if ( !
ini_get('log_errors') )
ini_set('log_errors', true);
if ( !
ini_get('error_log') )
ini_set('error_log', $log);
}
}

public static function
err_handler($errno, $errstr, $errfile, $errline, $errcontext) {
$l = error_reporting();
if (
$l & $errno ) {

$exit = false;
switch (
$errno ) {
case
E_USER_ERROR:
$type = 'Fatal Error';
$exit = true;
break;
case
E_USER_WARNING:
case
E_WARNING:
$type = 'Warning';
break;
case
E_USER_NOTICE:
case
E_NOTICE:
case @
E_STRICT:
$type = 'Notice';
break;
case @
E_RECOVERABLE_ERROR:
$type = 'Catchable';
break;
default:
$type = 'Unknown Error';
$exit = true;
break;
}

$exception = new \ErrorException($type.': '.$errstr, 0, $errno, $errfile, $errline);

if (
$exit ) {
exc_handler($exception);
exit();
}
else
throw
$exception;
}
return
false;
}

function
exc_handler($exception) {
$log = $exception->getMessage() . "\n" . $exception->getTraceAsString() . LINEBREAK;
if (
ini_get('log_errors') )
error_log($log, 0);
print(
"Unhandled Exception" . (DEBUG ? " - $log" : ''));
}

}
?>
18
triplepoint at gmail dot com
14 年前
如下所述,務必了解除非被捕獲,否則任何拋出的例外都會停止腳本執行。因此,將每個 notice、warning 或 error 轉換為 ErrorException,會在觸發像 E_USER_NOTICE 這樣無害的事件時,停止你的腳本。

在我看來,ErrorException 類別的最佳使用方式如下:

<?php
function custom_error_handler($number, $string, $file, $line, $context)
{
// 判斷此錯誤是否為 php 設定中啟用的錯誤之一 (php.ini, .htaccess 等)
$error_is_enabled = (bool)($number & ini_get('error_reporting') );

// -- 嚴重錯誤
// 拋出 ErrorException,由該內容中可用的任何例外處理邏輯處理
if( in_array($number, array(E_USER_ERROR, E_RECOVERABLE_ERROR)) && $error_is_enabled ) {
throw new
ErrorException($errstr, 0, $errno, $errfile, $errline);
}

// -- 非嚴重錯誤/警告/通知
// 如果啟用錯誤,則記錄錯誤,否則直接忽略
else if( $error_is_enabled ) {
error_log( $string, 0 );
return
false; // 確保適當的情況下,此訊息會進入 $php_errormsg
}
}
?>

將此函式設定為錯誤處理常式,只會針對 E_USER_ERROR 和 E_RECOVERABLE_ERROR 拋出 ErrorException,而其他已啟用的錯誤類型只會透過 error_log() 記錄。

再次值得注意的是,無論你做什麼,"E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING 和大部分的 E_STRICT" 永遠不會到達你的自訂錯誤處理常式,因此不會轉換為 ErrorException。請相應地規劃。
7
luke at cywh dot com
15 年前
為了補充 chris AT cmbuckley DOT co DOT uk 關於 ErrorException 類別引數問題的評論

我注意到問題出在 ErrorException 類別本身,而不是 Exception 類別。當僅使用 exception 類別時,就不再有這個問題。除了引數問題外,在堆疊追蹤中 Exception 和 ErrorException 之間的唯一區別是,引數從錯誤處理常式的例外函式中省略。我不確定這是否是故意的,但無論如何顯示此資訊應該無妨。

因此,你可以忽略這個損壞的擴展類別,自己建立一個擴展類別,並完全避免這個問題

<?php

header
('Content-Type: text/plain');

class
ErrorHandler extends Exception {
protected
$severity;

public function
__construct($message, $code, $severity, $filename, $lineno) {
$this->message = $message;
$this->code = $code;
$this->severity = $severity;
$this->file = $filename;
$this->line = $lineno;
}

public function
getSeverity() {
return
$this->severity;
}
}

function
exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new
ErrorHandler($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("exception_error_handler", E_ALL);

function
A() {
$foo->bar; // 故意造成錯誤
}

function
B($c) {
A();
}

try {
B('foobar');
} catch (
Exception $e) {
var_dump($e->getTrace());
}

?>

我唯一希望可以做的是移除錯誤處理常式函式的條目,因為它與實際問題無關。也許他們試圖用 ErrorException 類別做這件事?無論如何,你都無法更改它,因為追蹤函式是 final 的,而變數是私有的。
3
Christopher Marshall
3 年前
繼續 triplepoint at gmail dot com 的觀點

我目前使用 PHP 7.4.0,並嘗試在我的應用程式中導入錯誤處理、例外處理和嚴重錯誤例外處理。網路上關於使用 PHP 7 和 8 的新變更來處理錯誤的資訊大多已過時,這使得在最好的情況下也很難理解所有內容。

然而,我發現使用 register_shutdown_function 來處理嚴重錯誤例外時,它會如預期般運作。set_exception_handler 也完美地協同運作。問題出在使用 set_error_handler 時,並且觸發了自訂錯誤(例如使用 trigger_error)- 即使您使用的是 E_ERROR 或 E_USER_ERROR。

這是因為 PHP 會嘗試在關閉之前以及 register_shutdown_function 實際介入之前處理錯誤。因此,如果您對例外、錯誤和嚴重錯誤例外使用不同的方法,請務必留意這一點。您需要像以前一樣明確地捕獲錯誤,並從您的錯誤處理函數中返回,以便嚴重錯誤例外處理程序能正確啟動。

不客氣 .....

<?php
/**
* 我們以不同的方式處理基本錯誤
*/
public static function errorHandler($errStatus, $errMsg = '未知錯誤', $errFile = '未知', $errLine = 0)
{
/**
* 因為我們使用 set_error_handler,PHP 嘗試
* 變得聰明,並將嚴重錯誤和其他「錯誤」
* (即 trigger_error)路由到這裡,然後才到
* register_shutdown_function,因此我們需要確保這些
* 以正確的方式被捕獲和處理
*
* @See https://php.dev.org.tw/manual/en/class.errorexception.php#95415
*/
if (\in_array($errStatus, [E_ERROR, E_PARSE, E_CORE_ERROR, E_USER_ERROR, E_ERROR]))
{
return;
}

/* 以您想要的方式處理其他所有內容 */
}
-4
Kevin
7 年前
我一直在 Google 上搜尋如何將 E_NOTICE 錯誤轉換為例外,我想我終於找到一個乾淨的方法來做到這一點

@include "errorcausingcode.php";

$lastError = error_get_last();
if ( !empty( $lastError ) ) {
throw new TemplateRenderingException($lastError['message'], $lastError['type']);
}

基本上,如果它不是系統停止錯誤,它很可能會命中這裡。然後您可以拋出您想要的任何例外。現在,當然,這是如果您有需要。我這樣做是因為我想在發生錯誤跳過它時清理我的輸出緩衝區。
To Top