简体   繁体   English

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

[英]Can I try/catch a warning?

I need to catch some warnings being thrown from some php native functions and then handle them.我需要捕捉一些 php 本机函数引发的一些警告,然后处理它们。

Specifically:具体来说:

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

It throws a warning when the DNS query fails.当 DNS 查询失败时,它会引发警告。

try / catch doesn't work because a warning is not an exception. try / catch不起作用,因为警告不是异常。

I now have 2 options:我现在有两个选择:

  1. set_error_handler seems like overkill because I have to use it to filter every warning in the page (is this true?); set_error_handler似乎有点矫枉过正,因为我必须用它来过滤页面中的每个警告(这是真的吗?);

  2. Adjust error reporting/display so these warnings don't get echoed to screen, then check the return value;调整错误报告/显示,使这些警告不会在屏幕上回显,然后检查返回值; if it's false , no records is found for hostname.如果为false ,则找不到主机名的记录。

What's the best practice here?这里的最佳做法是什么?

Set and restore error handler设置和恢复错误处理程序

One possibility is to set your own error handler before the call and restore the previous error handler later with restore_error_handler() .一种可能性是在调用之前设置您自己的错误处理程序,并稍后使用restore_error_handler()恢复以前的错误处理程序。

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

You could build on this idea and write a re-usable error handler that logs the errors for you.您可以基于这个想法编写一个可重用的错误处理程序来为您记录错误。

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

Turning errors into exceptions将错误转化为异常

You can use set_error_handler() and the ErrorException class to turn all php errors into exceptions.您可以使用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) {
    // ...
}

The important thing to note when using your own error handler is that it will bypass the error_reporting setting and pass all errors (notices, warnings, etc.) to your error handler.使用您自己的错误处理程序时要注意的重要一点是,它将绕过error_reporting设置并将所有错误(通知、警告等)传递给您的错误处理程序。 You can set a second argument on set_error_handler() to define which error types you want to receive, or access the current setting using ... = error_reporting() inside the error handler.您可以在set_error_handler()上设置第二个参数来定义要接收的错误类型,或者在错误处理程序中使用... = error_reporting()访问当前设置。

Suppressing the warning抑制警告

Another possibility is to suppress the call with the @ operator and check the return value of dns_get_record() afterwards.另一种可能性是使用 @ 运算符抑制调用,然后检查dns_get_record()的返回值。 But I'd advise against this as errors/warnings are triggered to be handled, not to be suppressed.但我建议不要这样做,因为错误/警告会被触发以进行处理,而不是被抑制。

The solution that really works turned out to be setting simple error handler with E_WARNING parameter, like so:真正有效的解决方案是使用E_WARNING参数设置简单的错误处理程序,如下所示:

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

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

Be careful with the @ operator - while it suppresses warnings it also suppresses fatal errors.小心@运算符- 虽然它会抑制警告,但也会抑制致命错误。 I spent a lot of time debugging a problem in a system where someone had written @mysql_query( '...' ) and the problem was that mysql support was not loaded into PHP and it threw a silent fatal error.我花了很多时间在有人编写@mysql_query( '...' )的系统中调试一个问题,问题是 mysql 支持没有加载到 PHP 中,它引发了一个无声的致命错误。 It will be safe for those things that are part of the PHP core but please use it with care.对于属于 PHP 核心的那些东西是安全的,但小心使用。

bob@mypc:~$ php -a
Interactive shell

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

No further output - good luck debugging this!没有进一步的输出 - 祝你调试好运!

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:~$ 

This time we can see why it failed.这次我们可以看到为什么它失败了。

I wanted to try/catch a warning, but at the same time keep the usual warning/error logging (eg in /var/log/apache2/error.log );我想尝试/捕获警告,但同时保留通常的警告/错误记录(例如在/var/log/apache2/error.log中); for which the handler has to return false .处理程序必须为其返回false However, since the "throw new..." statement basically interrupts the execution, one then has to do the "wrap in function" trick, also discussed in:但是,由于“throw new...”语句基本上会中断执行,因此必须执行“wrap in function”技巧,也在以下内容中讨论:

Is there a static way to throw exception in php 有没有一种静态方法在php中抛出异常

Or, in brief:或者,简而言之:

  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();
    // ...
  }

EDIT: after closer inspection, it turns out it doesn't work: the " return false && throwErrorException ... " will, basically, not throw the exception, and just log in the error log;编辑:仔细检查后发现它不起作用:“ return false && throwErrorException ... ”基本上不会抛出异常,而只是登录错误日志; removing the " false && " part, as in " return throwErrorException ... ", will make the exception throwing work, but will then not log in the error_log... I'd still keep this posted, though, as I haven't seen this behavior documented elsewhere.删除“ false && ”部分,如“ return throwErrorException ... ”,将使异常抛出工作,但不会登录error_log ......不过,我仍然会保持这个发布,因为我没有' t 看到此行为记录在其他地方。

Combining these lines of code around a file_get_contents() call to an external url helped me handle warnings like " failed to open stream: Connection timed out " much better:将这些代码行组合到对外部 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();

This solution works within object context, too.该解决方案也适用于对象上下文。 You could use it in a function:您可以在函数中使用它:

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

Normaly you should never use @ unless this is the only solution.通常你不应该使用@,除非这是唯一的解决方案。 In that specific case the function dns_check_record should be use first to know if the record exists.在这种特定情况下,应首先使用函数 dns_check_record 来了解记录是否存在。

您可能应该尝试完全摆脱警告,但如果这不可能,您可以在调用前加上@(即@dns_get_record(...)),然后使用您可以获得的任何信息来确定警告是否发生或不。

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

try checking whether it returns some boolean value then you can simply put it as a condition.尝试检查它是否返回一些布尔值,然后您可以简单地将其作为条件。 I encountered this with the oci_execute(...) which was returning some violation with my unique keys.我在 oci_execute(...) 遇到了这个问题,它使用我的唯一键返回了一些违规。

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

As of PHP8, you can do the following instead of setting error handlers to catch Errors and Warnings.从 PHP8 开始,您可以执行以下操作,而不是设置错误处理程序来捕获错误和警告。 I Believe in PHP 7.something you could catch some Errors.我相信 PHP 7.something 你可以捕捉到一些错误。

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

You should generally be somewhere that you can pass or access a logger if you bulkhead in this way, as it can obfuscate coder errors, such as passing incorrectly typed parameters to a method, and mask a variety of other problems.如果以这种方式设置障碍,您通常应该在可以传递或访问记录器的地方,因为它可以混淆编码器错误,例如将错误类型的参数传递给方法,并掩盖各种其他问题。

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

Not sure if notices are caught (likely not), but you can likely solve around examples like this one, by thinking a little more about what you are looking to do.不确定通知是否被捕获(可能不会),但您可以通过更多地考虑您想要做什么来解决像这样的示例。

Both builder pattern, and options patterns provide solutions for this where prior to the site of call, which can be a private function or just after validity checks, you can throw a real custom exception that is attributable only to your code.构建器模式和选项模式都为此提供了解决方案,在调用站点之前(可以是私有函数或在有效性检查之后),您可以抛出仅归因于您的代码的真正自定义异常。 That will make even built-in functions very safe to use.这将使内置函数的使用变得非常安全。

One other nice practice is to use either debug_backtrace , with DEBUG_BACKTRACE_IGNORE_ARGS or use the getTrace or getTraceAsString methods on the Throwable so that some of the context is preseved.另一种好的做法是使用debug_backtraceDEBUG_BACKTRACE_IGNORE_ARGS getTrace getTraceAsString方法,以便保留一些上下文。

FolderStructure文件夹结构

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

CustomException.php自定义异常.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");

just include above file into your script file like this只需像这样将上述文件包含到您的脚本文件中

index.php索引.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

Since PHP7 you can catch most errors and warnings like so:自 PHP7 以来,您可以捕获大多数错误和警告,如下所示:

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

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

I would only recommend using @ to suppress warnings when it's a straight forward operation (eg $prop = @($high/($width - $depth)); to skip division by zero warnings).我只建议在直接操作时使用 @ 来抑制警告(例如 $prop = @($high/($width - $depth)); 跳过除以零警告)。 However in most cases it's better to handle.但是在大多数情况下,最好处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM