【问题标题】:Can I try/catch a warning?我可以尝试/捕捉警告吗?
【发布时间】:2010-11-17 12:33:41
【问题描述】:

我需要捕捉一些 php 原生函数抛出的警告,然后处理它们。

具体来说:

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

DNS 查询失败时会抛出警告。

try/catch 不起作用,因为警告不是异常。

我现在有两个选择:

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

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

这里的最佳做法是什么?

【问题讨论】:

标签: php error-handling try-catch


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    设置和恢复错误处理程序

    一种可能性是在调用之前设置您自己的错误处理程序,稍后使用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() 的返回值。 但我建议不要这样做,因为错误/警告会被触发以进行处理,而不是被抑制。

    【讨论】:

    • 是否建议在函数调用之前设置我自己的错误处理程序,然后在错误检查完成后恢复_error_handler?
    • 如果有很多并发请求,这将是线程安全的,并且每个请求都执行 1.set_error_handler()。 2.doit 3.restore_error_handler ?
    • 谢谢;这有帮助。 (他们说 PHP 不是一场灾难。)
    • +1 用于避免使用 @ 来抑制错误。 E_WARNING 实际上是一个非致命错误。一般来说,您应该始终尝试适当地处理错误。如果您的应用程序需要使用 set_error_handler,那么就这样做。通常建议在生产环境中记录错误并禁用它们的显示。检查日志后,您可以查看在开发环境中进行更改的位置。我见过太多@fopen/@unlink 的实例,想知道为什么开发人员没有执行检查以避免错误或使用 set_error_handler 处理错误。
    • 关于将警告转化为异常的注意事项:警告不会中止您的应用程序——未捕获异常可以!
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      通常你不应该使用@,除非这是唯一的解决方案。在这种特定情况下,应首先使用函数 dns_check_record 来了解记录是否存在。

      【讨论】:

        【解决方案5】:

        小心@ 运算符 - 虽然它会抑制警告,但也会抑制致命错误。我花了很多时间在有人写了@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:~$ 
        

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

        【讨论】:

        • 自 PHP 7 起,致命错误不再被抑制。 @ 运算符只是抑制警告和通知。
        【解决方案6】:

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

        set_error_handler("warning_handler", E_WARNING);
        dns_get_record(...)
        restore_error_handler();
        
        function warning_handler($errno, $errstr) { 
        // do something
        }
        

        【讨论】:

        • 这里也可以使用匿名callable 代替带有函数声明的字符串
        • 谢谢,但是如何在关键块之后删除错误处理程序?
        • 太棒了!只是trow new \Exception($errstr, $errno);warning_handler 函数内。谢谢。
        • 这是最好的答案!
        【解决方案7】:

        我想尝试/捕获警告,但同时保留通常的警告/错误记录(例如在 /var/log/apache2/error.log 中);处理程序必须返回false。但是,由于“throw new...”语句基本上会中断执行,因此必须执行“wrap in function”技巧,也在:

        Is there a static way to throw exception in 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......不过,我仍然会保持这个发布,因为我没有看到其他地方记录了这种行为。

        【讨论】:

          【解决方案8】:

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

          【讨论】:

          • 这是您绝对不想使用 @ 的一次 - 您可以控制操作,并且可以在操作之前检查它是否被零除。
          【解决方案9】:

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

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

          【讨论】:

            【解决方案10】:

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

            【讨论】:

              【解决方案11】:

              文件夹结构

              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
              

              【讨论】:

                【解决方案12】:

                从 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

                不确定通知是否被捕获(可能不会),但您可以通过更多地考虑您想要做什么来解决类似这样的示例。

                构建器模式和选项模式都为此提供了解决方案,在调用站点之前(可以是私有函数或仅在有效性检查之后),您可以抛出仅归因于您的代码的真正自定义异常。这将使内置函数的使用变得非常安全。

                另一个不错的做法是在 Throwable 上使用 debug_backtraceDEBUG_BACKTRACE_IGNORE_ARGS 或使用 getTracegetTraceAsString 方法,以便保留一些上下文。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-04-02
                  • 1970-01-01
                  • 2010-11-23
                  相关资源
                  最近更新 更多