PHP 日本研討會 2024

set_error_handler

(PHP 4 >= 4.0.1、PHP 5、PHP 7、PHP 8)

set_error_handler設定使用者定義的錯誤處理函式

描述

set_error_handler(?callable $callback, int $error_levels = E_ALL): ?callable

設定使用者函式(callback)來處理腳本中的錯誤。

此函式可用於在執行時期定義自訂錯誤處理常式,例如在應用程式需要於發生嚴重錯誤時執行檔案/資料清理,或在特定條件下觸發錯誤(使用 trigger_error())時。

請務必記住,除非回呼函式傳回 false,否則對於 error_levels 所指定的錯誤類型,標準 PHP 錯誤處理常式將會被完全繞過。error_reporting() 設定將不會生效,且無論如何都會呼叫錯誤處理常式 - 然而,仍然可以讀取 error_reporting 的目前值並採取適當的動作。

另請注意,如果需要,處理常式有責任透過呼叫 exit() 來停止腳本的執行。如果錯誤處理常式函式傳回,腳本執行將會繼續執行導致錯誤的語句之後的下一個語句。

下列錯誤類型無法使用使用者定義的函式處理:E_ERRORE_PARSEE_CORE_ERRORE_CORE_WARNINGE_COMPILE_ERRORE_COMPILE_WARNING,無論它們在哪裡引發,以及大多數在呼叫 set_error_handler() 的檔案中引發的 E_STRICT

如果錯誤發生在腳本執行之前(例如在檔案上傳時),則無法呼叫自訂錯誤處理常式,因為它在那時尚未註冊。

參數

callback

如果傳遞 null,則處理常式會重設為預設狀態。否則,處理常式會是具有以下簽名的回呼

handler(
    int $errno,
    string $errstr,
    string $errfile = ?,
    int $errline = ?,
    array $errcontext = ?
): bool
errno
第一個參數 errno 將會被傳遞引發的錯誤層級,作為整數。
errstr
第二個參數 errstr 將會被傳遞錯誤訊息,作為字串。
errfile
如果回呼接受第三個參數 errfile,則會將錯誤引發的檔案名稱以字串形式傳遞給它。
errline
如果回呼接受第四個參數 errline,則會將錯誤引發的行號以整數形式傳遞給它。
errcontext
如果回呼接受第五個參數 errcontext,則會將一個指向錯誤發生時的作用符號表的陣列傳遞給它。換句話說,errcontext 將包含一個陣列,其中包含錯誤觸發時作用域中存在的每個變數。使用者錯誤處理常式不得修改錯誤內容。
警告

從 PHP 7.2.0 開始,此參數已被棄用,並在 PHP 8.0.0 中移除。如果函式定義此參數時沒有預設值,則在呼叫時會引發「引數過少」的錯誤。

如果函式傳回 false,則會繼續執行正常的錯誤處理常式。

error_levels

可用於遮蔽 callback 函式的觸發,就像 error_reporting ini 設定控制顯示哪些錯誤一樣。如果沒有設定此遮罩,則無論 error_reporting 設定為何,都會針對每個錯誤呼叫 callback

傳回值

傳回先前定義的錯誤處理常式(如果有的話)。如果使用內建錯誤處理常式,則會傳回 null。如果先前的錯誤處理常式是類別方法,則此函式會傳回包含類別和方法名稱的索引陣列。

變更記錄

版本 描述
8.0.0 errcontext 已被移除,且不再傳遞至使用者回呼。
7.2.0 errcontext 已被棄用。現在使用此參數會發出 E_DEPRECATED 通知。

範例

範例 #1 使用 set_error_handler()trigger_error() 進行錯誤處理

以下範例顯示透過觸發錯誤並使用使用者定義的函式處理它們來處理內部例外狀況

<?php
// 錯誤處理函式
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(
error_reporting() & $errno)) {
// 這個錯誤碼沒有包含在 error_reporting 中,所以讓它
// 交由標準的 PHP 錯誤處理器處理
return false;
}

// $errstr 可能需要跳脫字元:
$errstr = htmlspecialchars($errstr);

switch (
$errno) {
case
E_USER_ERROR:
echo
"<b>我的錯誤</b> [$errno] $errstr<br />\n";
echo
" 在檔案 $errfile 的第 $errline 行發生嚴重錯誤";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo
"中止執行...<br />\n";
exit(
1);

case
E_USER_WARNING:
echo
"<b>我的警告</b> [$errno] $errstr<br />\n";
break;

case
E_USER_NOTICE:
echo
"<b>我的注意</b> [$errno] $errstr<br />\n";
break;

default:
echo
"未知的錯誤類型:[$errno] $errstr<br />\n";
break;
}

/* 不要執行 PHP 內部的錯誤處理器 */
return true;
}

// 測試錯誤處理的函式
function scale_by_log($vect, $scale)
{
if (!
is_numeric($scale) || $scale <= 0) {
trigger_error("x <= 0 的 log(x) 未定義,您使用了:scale = $scale", E_USER_ERROR);
}

if (!
is_array($vect)) {
trigger_error("輸入向量不正確,應為數值陣列", E_USER_WARNING);
return
null;
}

$temp = array();
foreach(
$vect as $pos => $value) {
if (!
is_numeric($value)) {
trigger_error("位置 $pos 的值不是數字,將使用 0 (零)", E_USER_NOTICE);
$value = 0;
}
$temp[$pos] = log($scale) * $value;
}

return
$temp;
}

// 設定為使用者自訂的錯誤處理器
$old_error_handler = set_error_handler("myErrorHandler");

// 觸發一些錯誤,首先定義一個包含非數字項目的混合陣列
echo "向量 a\n";
$a = array(2, 3, "foo", 5.5, 43.3, 21.11);
print_r($a);

// 現在產生第二個陣列
echo "----\n向量 b - 一個注意訊息 (b = log(PI) * a)\n";
/* 位置 $pos 的值不是數字,將使用 0 (零) */
$b = scale_by_log($a, M_PI);
print_r($b);

// 這會有問題,我們傳遞了一個字串而不是陣列
echo "----\n向量 c - 一個警告訊息\n";
/* 輸入向量不正確,應為數值陣列 */
$c = scale_by_log("不是陣列", 2.3);
var_dump($c); // NULL

// 這是一個嚴重錯誤,零或負數的對數是未定義的
echo "----\n向量 d - 嚴重錯誤\n";
/* x <= 0 的 log(x) 未定義,您使用了:scale = $scale" */
$d = scale_by_log($a, -2.5);
var_dump($d); // 永遠不會執行到
?>

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

vector a
Array
(
    [0] => 2
    [1] => 3
    [2] => foo
    [3] => 5.5
    [4] => 43.3
    [5] => 21.11
)
----
vector b - a notice (b = log(PI) * a)
<b>My NOTICE</b> [1024] Value at position 2 is not a number, using 0 (zero)<br />
Array
(
    [0] => 2.2894597716988
    [1] => 3.4341896575482
    [2] => 0
    [3] => 6.2960143721717
    [4] => 49.566804057279
    [5] => 24.165247890281
)
----
vector c - a warning
<b>My WARNING</b> [512] Incorrect input vector, array of values expected<br />
NULL
----
vector d - fatal error
<b>My ERROR</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br />
  Fatal error on line 35 in file trigger_error.php, PHP 5.2.1 (FreeBSD)<br />
Aborting...<br />

參見

新增註解

使用者貢獻的註解 35 條註解

Philip
11 年前
單靠此函式無法捕獲嚴重錯誤,有一個簡單的解決方法。以下是我的 error.php 檔案的一部分,用於處理應用程式中的錯誤和例外。在有人抱怨之前,我先補充說明,我不介意使用全域變數,這個檔案是我迷你框架的一部分,而且如果沒有 'config' 變數,應用程式無論如何都會崩潰。

<?php

/**
* 錯誤處理器,將流程傳遞給異常記錄器,並使用新的 ErrorException。
*/
function log_error( $num, $str, $file, $line, $context = null )
{
log_exception( new ErrorException( $str, 0, $num, $file, $line ) );
}

/**
* 未捕獲的異常處理器。
*/
function log_exception( Exception $e )
{
global
$config;

if (
$config["debug"] == true )
{
print
"<div style='text-align: center;'>";
print
"<h2 style='color: rgb(190, 50, 50);'>發生異常:</h2>";
print
"<table style='width: 800px; display: inline-block;'>";
print
"<tr style='background-color:rgb(230,230,230);'><th style='width: 80px;'>類型</th><td>" . get_class( $e ) . "</td></tr>";
print
"<tr style='background-color:rgb(240,240,240);'><th>訊息</th><td>{$e->getMessage()}</td></tr>";
print
"<tr style='background-color:rgb(230,230,230);'><th>檔案</th><td>{$e->getFile()}</td></tr>";
print
"<tr style='background-color:rgb(240,240,240);'><th>行號</th><td>{$e->getLine()}</td></tr>";
print
"</table></div>";
}
else
{
$message = "類型: " . get_class( $e ) . "; 訊息: {$e->getMessage()}; 檔案: {$e->getFile()}; 行號: {$e->getLine()};";
file_put_contents( $config["app_dir"] . "/tmp/logs/exceptions.log", $message . PHP_EOL, FILE_APPEND );
header( "Location: {$config["error_page"]}" );
}

exit();
}

/**
* 檢查是否發生嚴重錯誤,用於解決 set_error_handler 無法處理嚴重錯誤的問題。
*/
function check_for_fatal()
{
$error = error_get_last();
if (
$error["type"] == E_ERROR )
log_error( $error["type"], $error["message"], $error["file"], $error["line"] );
}

register_shutdown_function( "check_for_fatal" );
set_error_handler( "log_error" );
set_exception_handler( "log_exception" );
ini_set( "display_errors", "off" );
error_reporting( E_ALL );
elad dot yosifon at gmail dot com
11 年前
<?php
/**
* 根據 E_* 錯誤類型拋出例外
*/
set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
// 錯誤被 @ 運算子抑制了
if (0 === error_reporting()) { return false;}
switch(
$err_severity)
{
case
E_ERROR: throw new ErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_WARNING: throw new WarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_PARSE: throw new ParseException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_NOTICE: throw new NoticeException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_CORE_ERROR: throw new CoreErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_CORE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_COMPILE_ERROR: throw new CompileErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_COMPILE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_ERROR: throw new UserErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_WARNING: throw new UserWarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_NOTICE: throw new UserNoticeException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_STRICT: throw new StrictException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_RECOVERABLE_ERROR: throw new RecoverableErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_DEPRECATED: throw new DeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_DEPRECATED: throw new UserDeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line);
}
});

class
WarningException extends ErrorException {}
class
ParseException extends ErrorException {}
class
NoticeException extends ErrorException {}
class
CoreErrorException extends ErrorException {}
class
CoreWarningException extends ErrorException {}
class
CompileErrorException extends ErrorException {}
class
CompileWarningException extends ErrorException {}
class
UserErrorException extends ErrorException {}
class
UserWarningException extends ErrorException {}
class
UserNoticeException extends ErrorException {}
class
StrictException extends ErrorException {}
class
RecoverableErrorException extends ErrorException {}
class
DeprecatedException extends ErrorException {}
class
UserDeprecatedException extends ErrorException {}
aditycse at gmail dot com
8 年前
<?php
/**
* 用於記錄所有 PHP 通知、警告等訊息到檔案中,當錯誤回報功能開啟且 display_errors 關閉時
* @uses 用於生產環境中,記錄 PHP 程式碼的所有錯誤類型到檔案中,以利後續除錯和程式碼效能分析
* @author Aditya Mehrotra<aditycse@gmail.com>
*/
error_reporting(E_ALL);
ini_set("display_errors", "off");
define('ERROR_LOG_FILE', '/var/www/error.log');

/**
* 自訂錯誤處理函式
* @param integer $code
* @param string $description
* @param string $file
* @param interger $line
* @param mixed $context
* @return boolean
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
$displayErrors = ini_get("display_errors");
$displayErrors = strtolower($displayErrors);
if (
error_reporting() === 0 || $displayErrors === "on") {
return
false;
}
list(
$error, $log) = mapErrorCode($code);
$data = array(
'level' => $log,
'code' => $code,
'error' => $error,
'description' => $description,
'file' => $file,
'line' => $line,
'context' => $context,
'path' => $file,
'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
);
return
fileLog($data);
}

/**
* 此方法用於將資料寫入檔案
* @param mixed $logData
* @param string $fileName
* @return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
$fh = fopen($fileName, 'a+');
if (
is_array($logData)) {
$logData = print_r($logData, 1);
}
$status = fwrite($fh, $logData);
fclose($fh);
return (
$status) ? true : false;
}

/**
* 將錯誤代碼對應到錯誤文字和記錄位置。
*
* @param int $code 要對應的錯誤代碼
* @return array 包含錯誤文字和記錄位置的陣列。
*/
function mapErrorCode($code) {
$error = $log = null;
switch (
$code) {
case
E_PARSE:
case
E_ERROR:
case
E_CORE_ERROR:
case
E_COMPILE_ERROR:
case
E_USER_ERROR:
$error = '嚴重錯誤';
$log = LOG_ERR;
break;
case
E_WARNING:
case
E_USER_WARNING:
case
E_COMPILE_WARNING:
case
E_RECOVERABLE_ERROR:
$error = '警告';
$log = LOG_WARNING;
break;
case
E_NOTICE:
case
E_USER_NOTICE:
$error = '注意';
$log = LOG_NOTICE;
break;
case
E_STRICT:
$error = '嚴格';
$log = LOG_NOTICE;
break;
case
E_DEPRECATED:
case
E_USER_DEPRECATED:
$error = '已棄用';
$log = LOG_NOTICE;
break;
default :
break;
}
return array(
$error, $log);
}

//呼叫自訂錯誤處理函式
set_error_handler("handleError");

print_r($arra); //未定義變數
print_r($dssdfdfgg); //未定義變數
include_once 'file.php'; //沒有此檔案或目錄
?>
steve962 at gmail dot com
6 年前
使用此函式的回傳值時請小心。因為它會回傳舊的處理器,你可能會想做類似以下的事情:

<?php
function do_something()
{
$old = set_error_handler(“my_error_handler”);
// 執行一些你希望由 my_error_handler 處理的事情
set_error_handler($old);
}
?>

這樣做會有效,但它會反咬你一口,因為每次你這樣做,都會導致記憶體洩漏,因為舊的錯誤處理器會被放到堆疊中,供 restore_error_handler() 函式使用。

所以,請務必改用該函式來還原舊的錯誤處理器

<?php
function do_something()
{
set_error_handler(“my_error_handler”);
// 執行一些你希望由 my_error_handler 處理的事情
restore_error_handler();
}
?>
dannykopping at gmail dot com
10 年前
請記住,當嘗試在 PHP >= 5.3 的命名空間類別上設定靜態定義的錯誤處理器時,您需要使用類別的命名空間

<?php
set_error_handler
('\\My\\Namespace\\Bob::errorHandler');
?>
Klauss
7 年前
大家好。我不知道這是舊版本中的舊行為,但目前,您可以將例外和錯誤處理器設定為私有或受保護的方法,前提是您在可以存取該方法的環境中呼叫 `set_exception_handler()` 或 `set_error_handler()`。

範例
<?PHP
$Handler
= new class ()
{
public function
__construct ()
{
set_error_handler([&$this, 'HandleError']);
set_exception_handler([&$this, 'HandleException']);
}
protected function
HandleError ( $Code, $Message, $File = null, $Line = 0, $Context = [] )
{
// 在這裡處理錯誤。
}
private function
HandleException ( $Exception )
{
// 在這裡處理例外。
}
}
?>

注意:這些方法必須符合回呼參數的簽名。
Jacob Slomp
11 年前
如果您不希望客戶看到錯誤,並且希望比他們早一步知道問題,這可能會很方便。

即使是解析錯誤,它也會以電子郵件通知您錯誤。

set_error_handler() 對於我想要的功能不起作用。

<?php
ini_set
('log_errors',TRUE);
ini_set('error_log','tiny_uploads/errors.txt');

if(
$_SERVER['REMOTE_ADDR'] != "您的 IP 位址"){
ini_set('display_errors',false);
}

function
byebye(){

$dir = dirname(__FILE__);
if(
file_exists($dir."/tiny_uploads/errors.txt")){

$errors = file_get_contents($dir."/tiny_uploads/errors.txt");

if(
trim($errors)){

$head = "From: php_errors@".str_replace('www.','',$_SERVER['HTTP_HOST'])."\r\n";

$errors .= "---------------------------------------------\n\n";

$errors .= "\n\n伺服器資訊:\n\n".print_r($_SERVER, 1)."\n\n";
$errors .= "---------------------------------------------\n\n";

$errors .= "\n\nCOOKIE:\n\n".print_r($_COOKIE, 1)."\n\n";
$errors .= "---------------------------------------------\n\n";

$errors .= "\n\nPOST:\n\n".print_r($_POST, 1)."\n\n";
$errors .= "---------------------------------------------\n\n";

$errors .= "\n\nGET:\n\n".print_r($_GET, 1)."\n\n";


mail("YOUR@EMAIL.COM","PHP 錯誤 ".$_SERVER['HTTP_HOST']."", $errors , $head);

$fp = fopen($dir."/tiny_uploads/errors.txt","w+");
fputs($fp, "");
fclose($fp);
}
}
}
register_shutdown_function("byebye");
?>
kalle at meizo dot com
14 年前
對於那些正在/曾經尋找方法來取得致命錯誤(例如記憶體配置上限問題)的回溯追蹤的人來說,這可能會有幫助,這些錯誤無法使用使用者定義的函數處理,以精確找出問題所在。

在一個託管許多網站並共享通用 PHP 包含檔案的伺服器上,我設定在一個地方

<?php
@ini_set ("error_log", "/my/path/php.err-" . $_SERVER ["HTTP_HOST"] . "-" . $_SERVER ["REMOTE_ADDR"] . "-" . $_SERVER ["REQUEST_METHOD"] . "-" . str_replace ("/", "|", $_SERVER ["REQUEST_URI"]));
?>

我實際上也使用了我省略的軟體中的一些額外資訊,但這樣一來,您會發現錯誤會更整齊地排序,例如:-

/my/path/php.err-website.com-127.0.0.1-GET-path|index.html?xyz

這至少極大地幫助我進一步精確找出問題所在,而不是像以前一樣只看到記憶體不足,而不知道它是在哪個網站/頁面上發生的(因為 PHP 錯誤只包含執行記憶體不足時的最新 PHP 程式碼,通常只是共享的包含檔案,而不是實際的頁面)。
webmaster at paramiliar dot com
16 年前
我們需要使用錯誤處理程式來處理 SQL 錯誤,同時傳遞查詢以便記錄查詢,這就是我們想出來的,它有點像醜陋的橋樑,但它可以 100% 運作

<?php

function myErrorHandler($errno, $errstr, $errfile, $errline){
switch (
$errno) {
case
E_USER_ERROR:
if (
$errstr == "(SQL)"){
// 處理 SQL 錯誤
echo "<b>SQL 錯誤</b> [$errno] " . SQLMESSAGE . "<br />\n";
echo
"查詢 : " . SQLQUERY . "<br />\n";
echo
"在第 " . SQLERRORLINE . " 行,檔案 " . SQLERRORFILE . " 中 ";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo
"中止...<br />\n";
} else {
echo
"<b>我的錯誤</b> [$errno] $errstr<br />\n";
echo
" 在檔案 $errfile 中的第 $errline 行發生致命錯誤";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo
"中止...<br />\n";
}
exit(
1);
break;

case
E_USER_WARNING:
case
E_USER_NOTICE:
}
/* 不執行 PHP 內部錯誤處理程式 */
return true;
}

// 測試錯誤處理的函數

function sqlerrorhandler($ERROR, $QUERY, $PHPFILE, $LINE){
define("SQLQUERY", $QUERY);
define("SQLMESSAGE", $ERROR);
define("SQLERRORLINE", $LINE);
define("SQLERRORFILE", $PHPFILE);
trigger_error("(SQL)", E_USER_ERROR);
}

set_error_handler("myErrorHandler");

// 觸發 SQL 錯誤
$query = "SELECT * FROM tbl LIMIT 1";
$sql = @mysql_query($query)
or
sqlerrorhandler("(".mysql_errno().") ".mysql_error(), $query, $_SERVER['PHP_SELF'], __LINE__);


?>
wfinn at riverbed dot com
16 年前
「以下錯誤類型無法使用使用者定義的函數處理:E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING,以及在呼叫 set_error_handler() 的檔案中引發的大部分 E_STRICT。」

這不完全正確。set_error_handler() 無法處理它們,但 ob_start() 至少可以處理 E_ERROR。

<?php

function error_handler($output)
{
$error = error_get_last();
$output = "";
foreach (
$error as $info => $string)
$output .= "{$info}: {$string}\n";
return
$output;
}

ob_start('error_handler');

will_this_undefined_function_raise_an_error();

?>
dorphalsig at NOSPAMgmail dot com
13 年前
這個方法實際上可以捕捉到致命錯誤...

<?php
function shutdown()
{
$a=error_get_last();
if(
$a==null)
echo
"No errors";
else
print_r($a);

}
register_shutdown_function('shutdown');
ini_set('max_execution_time',1 );
sleep(3);
?>

它會輸出
Array ( [type] => 1 [message] => Maximum execution time of 1 second exceeded [file] => /path/to/file_name.php [line] => 136 )
nizamgok at gmail dot com
15 年前
我發現這裡有些人提到你無法捕捉到語法解析錯誤 (type 4, E_PARSE)。這不是真的。以下是我如何處理的。我希望這對某些人有幫助。

1) 在網站根目錄建立一個 "auto_prepend.php" 檔案並加入以下內容

<?php
register_shutdown_function
('error_alert');

function
error_alert()
{
if(
is_null($e = error_get_last()) === false)
{
mail('your.email@example.com', '來自 auto_prepend 的錯誤', print_r($e, true));
}
}
?>

2) 然後在網站根目錄的 .htaccess 檔案中加入 "php_value auto_prepend_file /www/auto_prepend.php" 這行。

* 請務必更改電子郵件地址和檔案路徑。
silkensedai at online dot fr
17 年前
我建立了一個錯誤處理器,它也會印出回溯追蹤,並且可以在某些錯誤發生時停止執行。如果你希望在發現每個錯誤時都停止執行,這會很有用。

<?php

function my_error_handler($errno, $errstr, $errfile, $errline){
$errno = $errno & error_reporting();
if(
$errno == 0) return;
if(!
defined('E_STRICT')) define('E_STRICT', 2048);
if(!
defined('E_RECOVERABLE_ERROR')) define('E_RECOVERABLE_ERROR', 4096);
print
"<pre>\n<b>";
switch(
$errno){
case
E_ERROR: print "錯誤"; break;
case
E_WARNING: print "警告"; break;
case
E_PARSE: print "語法解析錯誤"; break;
case
E_NOTICE: print "注意"; break;
case
E_CORE_ERROR: print "核心錯誤"; break;
case
E_CORE_WARNING: print "核心警告"; break;
case
E_COMPILE_ERROR: print "編譯錯誤"; break;
case
E_COMPILE_WARNING: print "編譯警告"; break;
case
E_USER_ERROR: print "使用者錯誤"; break;
case
E_USER_WARNING: print "使用者警告"; break;
case
E_USER_NOTICE: print "使用者注意"; break;
case
E_STRICT: print "嚴格注意"; break;
case
E_RECOVERABLE_ERROR: print "可恢復的錯誤"; break;
default: print
"未知的錯誤 ($errno)"; break;
}
print
":</b> <i>$errstr</i> 在 <b>$errfile</b> 的第 <b>$errline</b> 行\n";
if(
function_exists('debug_backtrace')){
//print "backtrace:\n";
$backtrace = debug_backtrace();
array_shift($backtrace);
foreach(
$backtrace as $i=>$l){
print
"[$i] 在函式 <b>{$l['class']}{$l['type']}{$l['function']}</b>";
if(
$l['file']) print " 在 <b>{$l['file']}</b>";
if(
$l['line']) print " 的第 <b>{$l['line']}</b> 行";
print
"\n";
}
}
print
"\n</pre>";
if(isset(
$GLOBALS['error_fatal'])){
if(
$GLOBALS['error_fatal'] & $errno) die('fatal');
}
}

function
error_fatal($mask = NULL){
if(!
is_null($mask)){
$GLOBALS['error_fatal'] = $mask;
}elseif(!isset(
$GLOBALS['die_on'])){
$GLOBALS['error_fatal'] = 0;
}
return
$GLOBALS['error_fatal'];
}

?>

用法

<?php
error_reporting
(E_ALL); // 會回報所有錯誤
set_error_handler('my_error_handler');
error_fatal(E_ALL^E_NOTICE); // 除了 E_NOTICE 之外,任何錯誤都會停止執行
?>
francois vespa
13 年前
這是在終端機 (CLI 介面) 中使用 php 時的注意事項。從命令列執行時,即使您有某種錯誤使用者處理函式,因此 STDERR 不會顯示,致命錯誤仍然會導致 PHP 直譯器顯示錯誤文字。您無法透過 php 做任何事情來解決這個問題。如果使用 UNIX/Linux,您可以在命令結尾加上 " 2>/dev/null" 來強制 STDERR 不顯示
Marcelius
15 年前
另一種捕捉 PHP 致命錯誤的方法

<?php
error_reporting
(E_ALL);
ini_set('display_errors', 0);

function
shutdown(){
$isError = false;
if (
$error = error_get_last()){
switch(
$error['type']){
case
E_ERROR:
case
E_CORE_ERROR:
case
E_COMPILE_ERROR:
case
E_USER_ERROR:
$isError = true;
break;
}
}

if (
$isError){
echo
"指令碼執行已停止 ({$error['message']})";
} else {
echo
"指令碼已完成";
}
}

register_shutdown_function('shutdown');
?>

請注意,這只會捕獲執行階段錯誤。因此,呼叫不存在的類別中的方法或宣告重複的函式,不會觸發關閉處理常式。
roy
22 年前
值得注意的是 - 如果您的錯誤處理常式本身拋出錯誤,PHP 會聰明地使用預設的錯誤處理常式來處理它。這樣,您就不會陷入無止境的死亡循環。至少在 PHP 4.2 中,這似乎是正確的。

(當然,有一些方法可以建立您的處理常式來處理這種情況,但為了通用目的,最好還是保持這種方式。)
Anonymous
18 年前
為了尊重 PHP 的 error_reporting() 函式的值,請使用

<?
if( ($level & error_reporting()) == 0 ) return;
?>
phpmanual at NO_SPHAMnetebb dot com
21 年前
給定以下程式碼

class CallbackClass {
function CallbackFunction() {
// 指向 $this
}

function StaticFunction() {
// 不指向 $this
}
}

function NonClassFunction() {
}

在 PHP 中,似乎有 3 種方法可以設定回呼函式(以 set_error_handler() 為例)

1: set_error_handler('NonClassFunction');

2: set_error_handler(array('CallbackClass', 'StaticFunction'));

3: $o =& new CallbackClass();
set_error_handler(array($o, 'CallbackFunction'));

以下也可能很有用

class CallbackClass {
function CallbackClass() {
set_error_handler(array(&$this, 'CallbackFunction')); // & 很重要
}

function CallbackFunction() {
// 指向 $this
}
}

文件中沒有清楚地概述這三個範例。
nicolas dot grekas+php at gmail dot com
11 年前
如果您想確保在不重設處理常式堆疊的情況下(如 set_error_handler(null) 所做的那樣)呼叫原生 PHP 錯誤處理常式,您可以簡單地呼叫 set_error_handler,並將 $error_types 設定為零。這在使用 error_get_last() 時尤其有用

<?php

// var_dump 或其他任何東西,因為它永遠不會被呼叫,因為設定為 0
set_error_handler('var_dump', 0);
@
$undef_var;
restore_error_handler();

// error_get_last() 現在處於已知的狀態:
// Undefined variable: undef_var

... // 做一些事情

$e = error_get_last();

...

?>
phil at propcom dot co dot uk
11 年前
重要的是要注意,如果 E_STRICT 錯誤觸發錯誤處理常式,而該處理常式又嘗試使用尚未載入的類別,則不會呼叫已註冊的 SPL 自動載入器。

在這種情況下,您應該手動載入錯誤處理常式所需的類別。
jtrick77 at gmail dot com
11 年前
對於任何對實際翻譯的錯誤代碼及其含義感興趣的人

1 E_ERROR (整數) 嚴重的執行階段錯誤。這些表示無法從中恢復的錯誤,例如記憶體配置問題。腳本的執行會中止。
2 E_WARNING (整數) 執行階段警告(非致命錯誤)。腳本的執行不會中止。
4 E_PARSE (整數) 編譯時語法錯誤。語法錯誤應僅由解析器產生。
8 E_NOTICE (整數) 執行階段通知。表示腳本遇到了一些可能表示錯誤的情況,但也可能在腳本正常執行過程中發生。
16 E_CORE_ERROR (整數) 在 PHP 初始啟動期間發生的嚴重錯誤。這類似於 E_ERROR,只是它是由 PHP 核心產生的。
32 E_CORE_WARNING (整數) 在 PHP 初始啟動期間發生的警告(非致命錯誤)。這類似於 E_WARNING,只是它是由 PHP 核心產生的。
64 E_COMPILE_ERROR (整數) 嚴重的編譯時錯誤。這類似於 E_ERROR,只是它是由 Zend Scripting Engine 產生的。
128 E_COMPILE_WARNING (整數) 編譯時警告(非致命錯誤)。這類似於 E_WARNING,只是它是由 Zend Scripting Engine 產生的。
256 E_USER_ERROR (整數) 使用者產生的錯誤訊息。這類似於 E_ERROR,只是它是在 PHP 程式碼中使用 PHP 函式 trigger_error() 產生的。
512 E_USER_WARNING (整數) 使用者產生的警告訊息。這類似於 E_WARNING,只是它是在 PHP 程式碼中使用 PHP 函式 trigger_error() 產生的。
1024 E_USER_NOTICE (整數) 使用者產生的通知訊息。這類似於 E_NOTICE,只是它是在 PHP 程式碼中使用 PHP 函式 trigger_error() 產生的。
2048 E_STRICT (整數) 啟用此選項可讓 PHP 建議您變更程式碼,以確保程式碼的最佳互通性和向前相容性。自 PHP 5 起,但在 PHP 5.4.0 之前未包含在 E_ALL 中
4096 E_RECOVERABLE_ERROR (整數) 可捕獲的嚴重錯誤。它表示發生了可能危險的錯誤,但沒有使 Engine 處於不穩定的狀態。如果錯誤未被使用者定義的處理常式捕獲(另請參閱 set_error_handler()),則應用程式會像 E_ERROR 一樣中止。自 PHP 5.2.0 起
8192 E_DEPRECATED (整數) 執行階段通知。啟用此選項可接收有關未來版本中將無法運作的程式碼的警告。自 PHP 5.3.0 起
16384 E_USER_DEPRECATED (整數) 使用者產生的警告訊息。這類似於 E_DEPRECATED,只是它是在 PHP 程式碼中使用 PHP 函式 trigger_error() 產生的。自 PHP 5.3.0 起
32767 E_ALL (整數) 支援的所有錯誤和警告,但 PHP 5.4.0 之前的 E_STRICT 除外。PHP 5.4.x 中為 32767,PHP 5.3.x 中為 30719,PHP 5.2.x 中為 6143,之前為 2047

(複製自 https://php.dev.org.tw/manual/en/errorfunc.constants.php
Anonymous
20 年前
當您讓 PHP 知道您有自訂的錯誤處理常式時,您似乎無法在類別內部 - 更新/設定新 - 變數。範例

<?php
class error {
var
$error;

function
error() {
$this->setIni(); // 這會導致 PHP 忽略對類別的所有其他變更。
}

function
handler() {
echo
$this->error.'!!';
}

function
setText($text) {
$this->error = $text;
}

function
setIni() {
set_error_handler(array($this, 'handler'));
}
}

$eh = new error;
$eh->setText('Error! <br>'); // 這不會被儲存

trigger_error('text', E_USER_ERROR);
// 列印 '!!'
?>

應該如何做
<?php
class error {
var
$error;

function
error() {
// 先不要讓 PHP 知道我們的錯誤處理常式
}

function
handler() {
echo
$this->error.'!!';
}

function
setText($text) {
$this->error = $text;
}

function
setIni() {
set_error_handler(array($this, 'handler'));
}
}

$eh = new error;
$eh->setText('Error! <br>'); // 這會運作
$eh->setIni(); // 在您準備好設定類別時呼叫此方法。所有其他將被呼叫的方法對 PHP 的錯誤處理無效

trigger_error('text', E_USER_ERROR);
// 列印 'Error! <br>!!'
?>
periklis
14 年前
如何在 php 5.2 中處理嚴重錯誤

<?php
register_shutdown_function
('shutdownFunction');
function
shutDownFunction() {
$error = error_get_last();
if (
$error['type'] == 1) {
//執行您的工作
}
}
?>
stepheneliotdewey at GmailDotCom
17 年前
手冊中指出

「errcontext 將包含觸發錯誤的範圍內存在的所有變數的陣列。使用者錯誤處理常式不得修改錯誤內容。」

但您知道為什麼您不得修改錯誤內容嗎?似乎 errcontext 是(如果不是字面上)透過取得 $GLOBALS 並將非全域的區域變數作為該陣列中的其他條目,然後*依參考*傳遞整個內容而建立的。

(如果您設定自訂錯誤處理常式,然後在其中使用 print_r($errcontext),您就可以證明這是正確的,因為 $GLOBALS 將被列印為遞迴陣列)。

換句話說,手冊中的語言具有誤導性,因為 errcontext 並不是錯誤*被*觸發時存在的變數的副本,而是呼叫腳本中*現有實時變數*的參考。

這包括超全域變數,例如 $_SERVER、$_POST、$_GET 等,以及範圍內所有使用者定義的變數。

這表示,如果您修改了 errcontext,您將會修改其他變數,而不僅僅是在您的錯誤處理函式生命週期內,還會影響呼叫腳本的生命週期。

如果您計劃在錯誤處理函式中停止執行,這並不重要,但如果您修改了 $errcontext 然後在處理錯誤後返回程式的正常流程,則會導致意料之外的行為,因為變數會保持修改狀態。例如,如果您在自訂錯誤處理函式中取消設定 $_SERVER,則一旦函式結束並且您返回產生錯誤的頁面,它將保持未設定狀態。

這應該在手冊中更清楚地說明,首先在上面的「參數」部分中,將 errhandler 標記為使用 &(符號)進行傳址呼叫,如下所示

handler ( int $errno, string $errstr [, string $errfile [, int $errline [, array &$errcontext]]] )
Steffen Staehle
19 年前
在將應用程式從 php 4.2.1 遷移到 php 4.3.9 時,我注意到使用 set_error_handler() 的兩個注意事項(我還沒有 php 5.0,這可能不適用於那裡!)。

1. 設定系統錯誤處理常式

如果您想在設定自己的錯誤處理常式後再次設定標準 php 錯誤處理常式,這在 php 4.2.1 中有效,方法是傳入空字串

<?php

function my_handler($log_level, $log_text, $error_file, $error_line)
{
// 如果這裡發生錯誤,將會呼叫標準錯誤處理常式
// (以避免遞迴)

// 做一些有用的事情
// ...
}

$last_handler = set_error_handler("my_handler");

// 在此之後,$last_handler == ""

// 還原標準錯誤處理常式

$last_handler = set_error_handler("");

// 在此之後,$last_handler == "my_handler"

?>

完全相同的程式碼現在在 php 4.3.9 中引發錯誤

set_error_handler() 預期參數 1,'',為有效的回呼函數

(由於第一次呼叫 set_error_handler() 的返回值仍然是空字串 "",我不知道如何再做到這一點。我並不需要這個,因為我像下面顯示的那樣使用我自己的處理常式,但了解這一點可能不錯。)

2. 設定您自己的「第二層」處理常式

如果您設定了自己的錯誤處理常式,並且想要在執行時將其替換為另一個處理常式(而不是標準 php 錯誤處理常式),請注意,在錯誤處理常式內部使用時,set_error_handler 的返回值為 "" 而不是前一個處理常式的名稱!這並不令人太驚訝,因為在執行您自定義的錯誤處理常式期間,php 會將其替換為標準 php 錯誤處理常式,以避免在處理常式內部出現問題時產生無限迴圈。如果您想要像我一樣的巢狀處理常式,這才有趣。我的設計背景

第一層處理常式:記錄到資料庫
第二層處理常式:記錄到平面檔案(如果記錄到資料庫失敗)
第三層處理常式:列印到 stdout(如果記錄到平面檔案失敗)(這最終是系統處理常式)。

<?php

function my_fallback_handler($log_level, $log_text, $error_file, $error_line)
{
// 如果這裡發生錯誤,將會呼叫標準錯誤處理常式
// (以避免遞迴)

// 做一些有用的事情
// ...

} // my_fallback_handler

function my_handler($log_level, $log_text, $error_file, $error_line)
{
// 如果這裡發生錯誤,將會呼叫標準錯誤處理常式
// (以避免遞迴)

// 但是我們想要有一個與標準錯誤處理常式不同的回退處理常式

$last_handler = set_error_handler("my_fallback_handler");

// 我預期 $last_handler == "my_handler"
// (在 my_handler() 外部會是這樣)
// 但在這裡它是空字串 ""

// 做一些有用的事情
// ...

// 現在再次設定第一層處理常式:
// (不要使用 $last_handler 作為參數,
// 因為它等於 "")

$last_handler = set_error_handler("my_handler");

}
// my_handler

$last_handler = set_error_handler("my_handler");

?>
a dot ross at amdev dot eu
5 年前
我缺少一種鏈式錯誤處理常式的方法。這不是 set_error_handler 提供的方法。您必須經歷一些障礙才能使其運作,但是透過使用函式的返回值,這是*非常*有可能的。這是一個例子

<?
$previous = set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) use (&$previous) {
/* 您的自訂錯誤處理程式碼在這裡。 */

// 如果定義了另一個錯誤處理常式,請呼叫它。
if ($previous) {
return $previous($errno, $errstr, $errfile, $errline, $errcontext);
} else {
// 使用標準 PHP 錯誤處理常式。
return false;
}
});
?>
gernovich at ya dot ru
1 天前
顯示所有錯誤處理常式
<?php
do {
$handler = set_error_handler(fn()=>false);
if(
$handler !== null) {
var_dump($handler);
restore_error_handler();
restore_error_handler();
}
} while (
$handler !== null);
?>
David Spector
4 年前
PHP 手冊對於如何處理 @ 運算符錯誤訊息說明得不是很清楚。

這是有效的程式碼

// 如果是 @ 運算符,則不執行任何操作
$errLevel=error_reporting(E_ALL);
if ($errLevel===0)
return true; // 忽略 @ 前綴的表達式錯誤
RGraph
1 年前
一個簡單的錯誤處理常式,使錯誤更加明顯

//
// 將錯誤處理常式設定為列印更多
// 可見的錯誤
//
set_error_handler(function ($errno, $errstr)
{
$str = '<div style="margin: 20px; background-color: #fdd; border: 3px solid red; padding: 10px; border-radius: 15px; line-height: 25px"><b>錯誤:</b>%s (錯誤層級:%s)</div>';

printf($str, $errstr, $errno);
});
kaioker
3 年前
超簡單的錯誤碼到人類可讀的轉換

function prettycode($code){
return $code == 0 ? "FATAL" : array_search($code, get_defined_constants(true)['Core']);
}
Alex M
3 年前
如果您是程式設計新手,並且想知道如何在 .htaccess 檔案中加入這些錯誤報告值的組合。這是一個小指南。

使用 PHP 函式 error_reporting,我們可以將選項與按位元 OR 運算符 | 相加。但是我們不能在 htaccess 檔案中使用這些常數,而且就我而言,我不知道如何加入按位元數字。

因此,解決方案可以是將選定的選項轉換為 int

echo (int)(E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR) ;
->263

然後您可以在 .htaccess 中使用 263

php_value error_reporting 263

在我的情況下,我需要顯示這些錯誤以用於我的偵錯伺服器。但是組合可能與我的不同。
chris at ocproducts dot com
8 年前
請注意,錯誤處理常式不會遞迴執行。如果在執行錯誤處理常式時(在錯誤處理常式本身或從中呼叫的程式碼中)發生錯誤,則不會再次呼叫錯誤處理常式。

這對 $php_errormsg 有微妙的影響。如果您依賴錯誤處理常式來抑制某些錯誤訊息進入 $php_errormsg(透過 return true; 因為 error_reporting 不會影響 $php_errormsg 的設定),則這對於在該錯誤處理常式內呼叫的任何程式碼都將不起作用。
mmtache at yahoo dot com
21 年前
@ 運算符將 error_reporting() 的值設定為 0。
這表示您也可以將其與您自己的錯誤處理常式一起使用。例如

function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars) {
if (error_reporting())
echo $errmsg;
}
set_error_handler("userErrorHandler");

function test(){
trigger_error("錯誤訊息", E_USER_WARNING);
}

@test(); // 不會輸出任何內容
ash
17 年前
處理錯誤和例外的錯誤處理函式;還具有包含可能的函式參數的回溯。

<?php

$cfg
= array();
$cfg['debug'] = 1;
$cfg['adminEmail'] = 'name@domain.tld';

function
errorHandler($errno, $errstr='', $errfile='', $errline='')
{
// 如果錯誤已被 @ 抑制
if (error_reporting() == 0) {
return;
}

global
$cfg;

// 檢查函式是否由例外呼叫
if(func_num_args() == 5) {
// 由 trigger_error() 呼叫
$exception = null;
list(
$errno, $errstr, $errfile, $errline) = func_get_args();

$backtrace = array_reverse(debug_backtrace());

}else {
// 捕獲到的例外
$exc = func_get_arg(0);
$errno = $exc->getCode();
$errstr = $exc->getMessage();
$errfile = $exc->getFile();
$errline = $exc->getLine();

$backtrace = $exc->getTrace();
}

$errorType = array (
E_ERROR => 'ERROR',
E_WARNING => 'WARNING',
E_PARSE => 'PARSING ERROR',
E_NOTICE => 'NOTICE',
E_CORE_ERROR => 'CORE ERROR',
E_CORE_WARNING => 'CORE WARNING',
E_COMPILE_ERROR => 'COMPILE ERROR',
E_COMPILE_WARNING => 'COMPILE WARNING',
E_USER_ERROR => 'USER ERROR',
E_USER_WARNING => 'USER WARNING',
E_USER_NOTICE => 'USER NOTICE',
E_STRICT => 'STRICT NOTICE',
E_RECOVERABLE_ERROR => 'RECOVERABLE ERROR'
);

// 建立錯誤訊息
if (array_key_exists($errno, $errorType)) {
$err = $errorType[$errno];
} else {
$err = 'CAUGHT EXCEPTION';
}

$errMsg = "$err: $errstr in $errfile on line $errline";

// 開始回溯追蹤
foreach ($backtrace as $v) {

if (isset(
$v['class'])) {

$trace = 'in class '.$v['class'].'::'.$v['function'].'(';

if (isset(
$v['args'])) {
$separator = '';

foreach(
$v['args'] as $arg ) {
$trace .= "$separator".getArgument($arg);
$separator = ', ';
}
}
$trace .= ')';
}

elseif (isset(
$v['function']) && empty($trace)) {
$trace = 'in function '.$v['function'].'(';
if (!empty(
$v['args'])) {

$separator = '';

foreach(
$v['args'] as $arg ) {
$trace .= "$separator".getArgument($arg);
$separator = ', ';
}
}
$trace .= ')';
}
}

// 如果啟用偵錯模式,則顯示錯誤訊息
if($cfg['debug'] == 1) {
echo
'<h2>Debug Msg</h2>'.nl2br($errMsg).'<br />
Trace: '
.nl2br($trace).'<br />';
}

// 該怎麼做
switch ($errno) {
case
E_NOTICE:
case
E_USER_NOTICE:
return;
break;

default:
if(
$cfg['debug'] == 0){
// 發送電子郵件給管理員
if(!empty($cfg['adminEmail'])) {
@
mail($cfg['adminEmail'],'critical error on '.$_SERVER['HTTP_HOST'], $errorText,
'From: Error Handler');
}
// 結束並顯示錯誤訊息
exit(displayClientMessage());
}
else
exit(
'<p>aborting.</p>');
break;

}

}
// errorHandler() 結束

function displayClientMessage()
{
echo
'some html page with error message';

}

function
getArgument($arg)
{
switch (
strtolower(gettype($arg))) {

case
'string':
return(
'"'.str_replace( array("\n"), array(''), $arg ).'"' );

case
'boolean':
return (bool)
$arg;

case
'object':
return
'object('.get_class($arg).')';

case
'array':
$ret = 'array(';
$separtor = '';

foreach (
$arg as $k => $v) {
$ret .= $separtor.getArgument($k).' => '.getArgument($v);
$separtor = ', ';
}
$ret .= ')';

return
$ret;

case
'resource':
return
'resource('.get_resource_type($arg).')';

default:
return
var_export($arg, true);
}
}

?>
devermin at ti0n dot net
14 年前
我在工作中有一些程式碼帶有錯誤,這些未捕獲的錯誤會導致完整性遺失(人們使用 file_get_contents 呼叫網路服務,該服務會靜默失敗,然後在資料庫中插入垃圾資料)。

這是我找到的解決方案,可將一組特定的錯誤轉換為例外,然後能夠根據類別選擇性地採取行動(使用錯誤代碼)。

<?php
ini_set
('error_reporting',E_ALL^E_NOTICE);

## 前 10 位元保留給初始錯誤編號
define('EMASK',(~0)<<10);
define('ECODEMASK',~EMASK);
## 分類
define('IOERROR', 1<<10);
define('EMPTYPARMS', 1<<11);
define('FAILURE', 1<<12);
## 字串錯誤模式 => 代碼

$catch_me=array(
"/^(file_get_contents)\((.*)\).*failed to open stream: (.*)/ " =>
array (
'mesg' => "IO::無法開啟串流",
'code' => IOERROR | FAILURE
),
"/^fopen\(.*\): Filename cannot be empty/" =>
array(
'msg' => "Parameters::empty",
'code' => EMPTYPARMS
)
);
function
error_2_exception($errno, $errstr, $errfile, $errline,$context) {
global
$catch_me;
foreach (
$catch_me as $regexp => $res) {
if(
preg_match($regexp,$errstr,$match)){
throw new
Exception($res['mesg'],$res['code']|( $errno & EMASK ) );
}
}
/* 切換回 PHP 內部錯誤處理器 */
return false;
}
## => 想要捕捉這個
$f=file_get_contents("mlsdkfm");
## 尚不希望打破現有的錯誤行為(因此未被捕捉)
$f=file_get_contents('');
## 魔術
set_error_handler("error_2_exception");
## 行為保持不變
$f=file_get_contents('');
try {
## 現在無法運作的網路服務會引發例外 \o/
$f=file_get_contents("mlsdkfm");
} catch(
Exception $e) {
## 而且我可以按類別分組我的例外
echo ( $e->getCode() & FAILURE ) ? "\nEPIC FAIL\n" : "\nbegnine";
}

?>
To Top