我可以尝试/捕捉警告吗?


358

我需要捕获一些从PHP本机函数抛出的警告,然后处理它们。

特别:

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

DNS查询失败时,它将引发警告。

try/ catch不起作用,因为警告也不例外。

我现在有2个选择:

  1. set_error_handler 似乎有点过分,因为我必须使用它来过滤页面中的每个警告(这是真的吗?);

  2. 调整错误报告/显示,以使这些警告不会在屏幕上显示,然后检查返回值;如果为false,则找不到主机名的记录。

最佳做法是什么?


1
stackoverflow.com/questions/136899/…是关于此类事情的很好的讨论。
Mez

下面有一个答案被删除了?是由所有者还是由某人?
user121196


@ user121196:是的。由所有者。
Lightness Races in Orbit

Answers:


373

设置和还原错误处理程序

一种可能是在调用之前设置您自己的错误处理程序,并稍后使用还原以前的错误处理程序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, $errcontext) {
    // 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设置并将所有错误(通知,警告等)传递给错误处理程序。您可以将第二个参数设置为on,set_error_handler()以定义要接收的错误类型,或者使用... = error_reporting()错误处理程序内部访问当前设置。

禁止警告

另一种可能性是使用@运算符禁止调用,然后检查after的返回值dns_get_record()但是我建议不要这样做,因为错误/警告会被触发来处理,而不是被抑制。


3
是否建议在函数调用之前先设置自己的错误处理程序,然后在完成错误检查后再设置restore_error_handler?
user121196

2
如果有多个并发请求,并且每个请求都执行1.set_error_handler(),这将是线程安全的。2.doit 3.restore_error_handler?
user121196

4
谢谢; 这会有所帮助。(他们说PHP并不是灾难。)
亚伦·米勒

2
+1以避免使用@来抑制错误。E_WARNING实际上是一个非致命错误。通常,您应该始终尝试适当地处理错误。如果您的应用程序需要使用set_error_handler,则需要这样做。通常建议记录错误并禁用生产环境中的错误显示。检查日志后,您可以看到在开发环境中的何处进行更改。我见过@ fopen / @ unlink的实例太多了,想知道为什么开发人员没有执行检查来避免错误或使用set_error_handler处理错误。
fyrye 2014年

5
关于将警告变为异常的说明:警告不会终止您的应用程序- 未捕获的异常会成功!
阿尔瓦罗·冈萨雷斯

149

真正有效的解决方案是使用E_WARNING参数设置简单的错误处理程序,如下所示:

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

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

4
callable可以在此处使用匿名而不是带有函数声明的字符串
vp_arth

谢谢,但是如何在关键块之后删除错误处理程序?
Yevgeniy Afanasyev 2015年

3
优秀的!只是trow new \Exception($errstr, $errno);内部warning_handler功能。谢谢。
弗拉基米尔·武卡纳克(Fladimir Vukanac)

这是最好的答案!
lewis4u

28

小心@操作员 -在抑制警告的同时,也抑制致命的错误。我花了很多时间在有人编写的系统中调试问题,@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:~$ 

这次我们可以看到失败的原因。


5

我想尝试/捕获警告,但同时保留通常的警告/错误记录(例如/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 ...不过,我仍将其保持张贴状态,因为我没有在其他地方看到此行为的记录。


4

您可能应该尝试完全消除警告,但是如果不可能,可以在呼叫之前添加@(即@dns_get_record(...)),然后使用可以获取的任何信息来确定警告是否发生或不。


4

通常,除非这是唯一的解决方案,否则永远不要使用@。在那种特定情况下,应该首先使用功能dns_check_record来知道记录是否存在。


3

将这些代码行与file_get_contents()对外部URL 的调用结合起来,可以帮助我更好地处理类似“ 无法打开流:连接超时 ”之类的警告:

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;
}

2

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


0

尝试检查它是否返回一些布尔值,然后就可以简单地将其作为条件。我在oci_execute(...)遇到了这个问题,该返回我的唯一键违规。

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

0

文件夹结构

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

CustomException.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");

只是将上述文件包含到您的脚本文件中,就像这样

index.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

-2

我只建议使用@禁止直接警告操作(例如$ prop = @($ high /($ width-$ depth));跳过除以零警告的警告)。但是,在大多数情况下,最好处理。


2
这是您绝对不想使用@的一次-您可以控制该操作,并且可以在执行操作之前检查它是否为零除。
Eborbob
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.