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

Can I try/catch a warning?

提问人:user121196 提问时间:8/7/2009 最后编辑:dreftymacuser121196 更新时间:7/5/2022 访问量:266803

问:

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

具体说来:

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

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

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

我现在有 2 个选择:

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

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

这里的最佳实践是什么?

php 错误处理 try-catch

评论

2赞 Mez 8/7/2009
stackoverflow.com/questions/136899/......是关于这样的事情的一个很好的讨论。
0赞 user121196 8/7/2009
下面有一个被删除的答案?是所有者还是某人?
0赞 dreftymac 10/12/2011
参见:stackoverflow.com/questions/1087365
0赞 Lightness Races in Orbit 2/2/2012
@user121196:是的。由业主。

答:

2赞 rpjohnst 8/7/2009 #1

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

425赞 Philippe Gerber 8/7/2009 #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();

将错误转化为异常

您可以使用 和 类将所有 php 错误转换为异常。set_error_handler()ErrorException

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_reportingset_error_handler()... = error_reporting()

禁止显示警告

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

评论

5赞 user121196 8/7/2009
是否建议在函数调用之前设置我自己的错误处理程序,然后在错误检查完成后restore_error_handler?
3赞 user121196 8/7/2009
如果有许多并发请求,并且每个请求都执行 1.set_error_handler(),这是否是线程安全的。2.doit 3.restore_error_handler ?
1赞 Philippe Gerber 8/7/2009
1. 这是否是好的做法?我真的不知道。但如果 Zend 的家伙使用这种方法,它就不会那么糟糕。2.每个请求都是独立的,所以不会有任何问题。
2赞 Will B. 1/22/2014
+1 用于避免使用 @ 来抑制错误。E_WARNING实际上是一个非致命错误。通常,您应该始终尝试适当地处理错误。如果您的应用程序需要使用 set_error_handler,请这样做。通常建议记录错误并禁用在生产环境中显示错误。检查日志后,您可以看到在开发环境中进行更改的位置。我看到@fopen/@unlink的情况太多了,想知道为什么开发人员没有执行检查以避免错误或使用 set_error_handler 处理错误。
7赞 Álvaro González 5/7/2015
关于将警告转换为异常的注意事项:警告不会中止您的应用,而未捕获的异常则可以!
3赞 Amber 8/7/2009 #3

如果失败,它应该返回 ,因此您可以禁止显示警告,然后检查返回值。dns_get_record()FALSE@

4赞 florynth 1/4/2011 #4

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

33赞 GuruBob 8/10/2011 #5

小心使用 @ 运算符 - 虽然它抑制警告,但它也抑制致命错误。我花了很多时间在一个有人编写的系统中调试一个问题,问题是 mysql 支持没有加载到 PHP 中,它抛出了一个无声的致命错误。对于那些属于PHP核心的东西来说,它是安全的,但请小心使用它。@mysql_query( '...' )

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

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

评论

0赞 Esteban 12/30/2021
从 PHP 7 开始,致命错误不再被禁止。@ 运算符只是禁止警告和通知。
177赞 Robert 6/26/2012 #6

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

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

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

评论

4赞 vp_arth 2/11/2015
也可以在这里使用 anonymous 代替带有函数声明的字符串callable
0赞 Yevgeniy Afanasyev 7/16/2015
谢谢,但是如何在关键块之后删除错误处理程序?
5赞 Vladimir Vukanac 7/30/2015
非常好!只是在功能内部。谢谢。trow new \Exception($errstr, $errno);warning_handler
2赞 lewis4u 3/25/2019
这是这里最好的答案!
6赞 sdaau 12/27/2013 #7

我想尝试/捕获警告,但同时保留通常的警告/错误日志记录(例如在 );处理程序必须返回 。然而,由于“抛出新的......”语句基本上中断了执行,然后必须执行“包装函数”技巧,在以下内容中也进行了讨论:/var/log/apache2/error.logfalse

有没有一种静态方法可以在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();
    // ...
  }

编辑:经过仔细检查,事实证明它不起作用:“”基本上不会抛出异常,而只是登录错误日志;删除 “” 部分,如 “”,将使异常抛出起作用,但随后不会登录error_log...不过,我仍然会保留此内容,因为我没有在其他地方看到这种行为的记录。return false && throwErrorException ...false &&return throwErrorException ...

评论

0赞 Elikill58 2/9/2023
我们怎样才能恢复这种变化?
0赞 AmigoJack 3/20/2023
根据布尔计算,逻辑永远不会超越,因为在链中遇到一次就足以确定它仍然存在。因此,之后不会计算文字、变量或函数。因此,顺序变得很重要。如果有的话,让它.这称为短路评估return false && &&false&&falsereturn func() && false
-1赞 tanovellino 9/2/2016 #8

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

评论

4赞 Eborbob 2/21/2017
这是一次你绝对不想使用 @ - 你可以控制操作,并且可以在执行之前检查它是否除以零。
1赞 gborjal 11/8/2017 #9

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

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
5赞 Bugfighter 6/29/2018 #10

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

评论

1赞 Roger Dueck 6/24/2022
处理函数 () 中的最后一个参数已在 PHP 8 (php.net/manual/en/function.set-error-handler.php) 中删除。$err_context
0赞 Juned Ansari 9/5/2019 #11

文件夹结构

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
2赞 MrMesees 11/5/2021 #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 上使用 , with 或使用 or 方法,以便预先设置一些上下文。debug_backtraceDEBUG_BACKTRACE_IGNORE_ARGSgetTracegetTraceAsString

-1赞 cprn 7/5/2022 #13

从 PHP7 开始,您可以捕获大多数错误和警告,如下所示:

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

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