我能听到警告吗?

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

具体地说:

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

当DNS查询失败时,它抛出一个警告。

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

我现在有两个选择:

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

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

这里的最佳实践是什么?

250460 次浏览

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

设置和恢复错误处理程序

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

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

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

小心使用@操作符 -虽然它抑制警告,它也抑制致命错误。我花了很多时间在一个系统中调试一个问题,有人写了@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:~$

这次我们可以看到为什么它失败了。

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

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


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

我想尝试/捕捉一个警告,但同时保持通常的警告/错误日志(例如在/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…尽管如此,我仍然会把这篇文章贴在网上,因为我还没有在其他地方看到这种行为的记录。

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

尝试检查它是否返回一些布尔值,然后你可以简单地把它作为一个条件。我遇到这与oci_execute(…)返回一些违反我的唯一键。

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

将这些围绕file_get_contents()调用外部url的代码行组合起来,帮助我更好地处理像“failed to open stream:连接超时”这样的警告:

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

FolderStructure

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

从PHP8开始,您可以执行以下操作,而不是设置错误处理程序来捕获错误和警告。我相信PHP 7。你可以捕捉一些错误。

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,或者在Throwable上使用getTracegetTraceAsString方法,这样可以保留一些上下文。

从PHP7开始,你可以像这样捕捉大多数错误和警告:

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

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