簡體   English   中英

我可以嘗試/捕捉警告嗎?

[英]Can I try/catch a warning?

我需要捕捉一些 php 本機函數引發的一些警告,然后處理它們。

具體來說:

array dns_get_record  ( string $hostname  [, int $type= DNS_ANY  [, array &$authns  [, array &$addtl  ]]] )

當 DNS 查詢失敗時,它會引發警告。

try / catch不起作用,因為警告不是異常。

我現在有兩個選擇:

  1. set_error_handler似乎有點矯枉過正,因為我必須用它來過濾頁面中的每個警告(這是真的嗎?);

  2. 調整錯誤報告/顯示,使這些警告不會在屏幕上回顯,然后檢查返回值; 如果為false ,則找不到主機名的記錄。

這里的最佳做法是什么?

設置和恢復錯誤處理程序

一種可能性是在調用之前設置您自己的錯誤處理程序,並稍后使用restore_error_handler()恢復以前的錯誤處理程序。

set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();

您可以基於這個想法編寫一個可重用的錯誤處理程序來為您記錄錯誤。

set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();

將錯誤轉化為異常

您可以使用set_error_handler()ErrorException類將所有 php 錯誤轉換為異常。

set_error_handler(function($errno, $errstr, $errfile, $errline) {
    // error was suppressed with the @-operator
    if (0 === error_reporting()) {
        return false;
    }
    
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
    dns_get_record();
} catch (ErrorException $e) {
    // ...
}

使用您自己的錯誤處理程序時要注意的重要一點是,它將繞過error_reporting設置並將所有錯誤(通知、警告等)傳遞給您的錯誤處理程序。 您可以在set_error_handler()上設置第二個參數來定義要接收的錯誤類型,或者在錯誤處理程序中使用... = error_reporting()訪問當前設置。

抑制警告

另一種可能性是使用 @ 運算符抑制調用,然后檢查dns_get_record()的返回值。 但我建議不要這樣做,因為錯誤/警告會被觸發以進行處理,而不是被抑制。

真正有效的解決方案是使用E_WARNING參數設置簡單的錯誤處理程序,如下所示:

set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();

function warning_handler($errno, $errstr) { 
// do something
}

小心@運算符- 雖然它會抑制警告,但也會抑制致命錯誤。 我花了很多時間在有人編寫@mysql_query( '...' )的系統中調試一個問題,問題是 mysql 支持沒有加載到 PHP 中,它引發了一個無聲的致命錯誤。 對於屬於 PHP 核心的那些東西是安全的,但小心使用。

bob@mypc:~$ php -a
Interactive shell

php > echo @something(); // this will just silently die...

沒有進一步的輸出 - 祝你調試好運!

bob@mypc:~$ php -a
Interactive shell

php > echo something(); // lets try it again but don't suppress the error
PHP Fatal error:  Call to undefined function something() in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
bob@mypc:~$ 

這次我們可以看到為什么它失敗了。

我想嘗試/捕獲警告,但同時保留通常的警告/錯誤記錄(例如在/var/log/apache2/error.log中); 處理程序必須為其返回false 但是,由於“throw new...”語句基本上會中斷執行,因此必須執行“wrap in function”技巧,也在以下內容中討論:

有沒有一種靜態方法在php中拋出異常

或者,簡而言之:

  function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
  }
  function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
    return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
    # error_log("AAA"); # will never run after throw
    /* Do execute PHP internal error handler */
    # return false; # will never run after throw
  }
  ...
  set_error_handler('warning_handler', E_WARNING);
  ...
  try {
    mkdir($path, 0777, true);
  } catch (Exception $e) {
    echo $e->getMessage();
    // ...
  }

編輯:仔細檢查后發現它不起作用:“ return false && throwErrorException ... ”基本上不會拋出異常,而只是登錄錯誤日志; 刪除“ false && ”部分,如“ return throwErrorException ... ”,將使異常拋出工作,但不會登錄error_log ......不過,我仍然會保持這個發布,因為我沒有' t 看到此行為記錄在其他地方。

將這些代碼行組合到對外部 url 的file_get_contents()調用周圍,可以幫助我更好地處理諸如“無法打開流:連接超時”之類的警告:

set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
    throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line );
}, E_WARNING);
try {
    $iResult = file_get_contents($sUrl);
} catch (Exception $e) {
    $this->sErrorMsg = $e->getMessage();
}
restore_error_handler();

該解決方案也適用於對象上下文。 您可以在函數中使用它:

public function myContentGetter($sUrl)
{
  ... code above ...
  return $iResult;
}

通常你不應該使用@,除非這是唯一的解決方案。 在這種特定情況下,應首先使用函數 dns_check_record 來了解記錄是否存在。

您可能應該嘗試完全擺脫警告,但如果這不可能,您可以在調用前加上@(即@dns_get_record(...)),然后使用您可以獲得的任何信息來確定警告是否發生或不。

如果dns_get_record()失敗,它應該返回FALSE ,因此您可以使用@抑制警告,然后檢查返回值。

嘗試檢查它是否返回一些布爾值,然后您可以簡單地將其作為條件。 我在 oci_execute(...) 遇到了這個問題,它使用我的唯一鍵返回了一些違規。

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}

從 PHP8 開始,您可以執行以下操作,而不是設置錯誤處理程序來捕獲錯誤和警告。 我相信 PHP 7.something 你可以捕捉到一些錯誤。

try {
    call_user_func('sprintf', array_merge([$string], $args));
} catch (Throwable $e) {
    $logger->info('mesage...');
}

如果以這種方式設置障礙,您通常應該在可以傳遞或訪問記錄器的地方,因為它可以混淆編碼器錯誤,例如將錯誤類型的參數傳遞給方法,並掩蓋各種其他問題。

https://php.watch/versions/8.0/internal-function-exceptions

不確定通知是否被捕獲(可能不會),但您可以通過更多地考慮您想要做什么來解決像這樣的示例。

構建器模式和選項模式都為此提供了解決方案,在調用站點之前(可以是私有函數或在有效性檢查之后),您可以拋出僅歸因於您的代碼的真正自定義異常。 這將使內置函數的使用變得非常安全。

另一種好的做法是使用debug_backtraceDEBUG_BACKTRACE_IGNORE_ARGS getTrace getTraceAsString方法,以便保留一些上下文。

文件夾結構

index.php //Script File
logs //Folder for log Every warning and Errors
CustomException.php //Custom exception File

自定義異常.php

/**
* Custom error handler
*/
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(
        'timestamp' => date("Y-m-d H:i:s:u", time()),
        'level' => $log,
        'code' => $code,
        'type' => $error,
        'description' => $description,
        'file' => $file,
        'line' => $line,
        'context' => $context,
        'path' => $file,
        'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
    );
    $data = array_map('htmlentities',$data);
    return fileLog(json_encode($data));
}

/**
* This method is used to write data in file
* @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 . "\n");
    fclose($fh);
//    $file = file_get_contents($filename);
//    $content = '[' . $file .']';
//    file_put_contents($content); 
    return ($status) ? true : false;
}

/**
* Map an error code into an Error word, and log location.
*
* @param int $code Error code to map
* @return array Array of error word, and log location.
*/
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 = 'Fatal Error';
            $log = LOG_ERR;
            break;
        case E_WARNING:
        case E_USER_WARNING:
        case E_COMPILE_WARNING:
        case E_RECOVERABLE_ERROR:
            $error = 'Warning';
            $log = LOG_WARNING;
            break;
        case E_NOTICE:
        case E_USER_NOTICE:
            $error = 'Notice';
            $log = LOG_NOTICE;
            break;
        case E_STRICT:
            $error = 'Strict';
            $log = LOG_NOTICE;
            break;
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            $error = 'Deprecated';
            $log = LOG_NOTICE;
            break;
        default :
            break;
    }
    return array($error, $log);
}
//calling custom error handler
set_error_handler("handleError");

只需像這樣將上述文件包含到您的腳本文件中

索引.php

error_reporting(E_ALL);
ini_set('display_errors', 'off');
define('ERROR_LOG_FILE', 'logs/app_errors.log');

include_once 'CustomException.php';
echo $a; // here undefined variable warning will be logged into logs/app_errors.log

自 PHP7 以來,您可以捕獲大多數錯誤和警告,如下所示:

try {
    whatever();
} catch (Throwable $e) {
}

更多: https ://www.php.net/manual/en/language.errors.php7.php

我只建議在直接操作時使用 @ 來抑制警告(例如 $prop = @($high/($width - $depth)); 跳過除以零警告)。 但是在大多數情況下,最好處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM