[英]Can I try/catch a warning?
我需要捕捉一些 php 本机函数引发的一些警告,然后处理它们。
具体来说:
array dns_get_record ( string $hostname [, int $type= DNS_ANY [, array &$authns [, array &$addtl ]]] )
当 DNS 查询失败时,它会引发警告。
try
/ catch
不起作用,因为警告不是异常。
我现在有两个选择:
set_error_handler
似乎有点矫枉过正,因为我必须用它来过滤页面中的每个警告(这是真的吗?);
调整错误报告/显示,使这些警告不会在屏幕上回显,然后检查返回值; 如果为false
,则找不到主机名的记录。
这里的最佳做法是什么?
设置和恢复错误处理程序
一种可能性是在调用之前设置您自己的错误处理程序,并稍后使用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()
的返回值。 但我建议不要这样做,因为错误/警告会被触发以进行处理,而不是被抑制。
真正有效的解决方案是使用E_WARNING
参数设置简单的错误处理程序,如下所示:
set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();
function warning_handler($errno, $errstr) {
// do something
}
小心@
运算符- 虽然它会抑制警告,但也会抑制致命错误。 我花了很多时间在有人编写@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:~$
这次我们可以看到为什么它失败了。
我想尝试/捕获警告,但同时保留通常的警告/错误记录(例如在/var/log/apache2/error.log
中); 处理程序必须为其返回false
。 但是,由于“throw new...”语句基本上会中断执行,因此必须执行“wrap in function”技巧,也在以下内容中讨论:
或者,简而言之:
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 ......不过,我仍然会保持这个发布,因为我没有' t 看到此行为记录在其他地方。
将这些代码行组合到对外部 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;
}
通常你不应该使用@,除非这是唯一的解决方案。 在这种特定情况下,应首先使用函数 dns_check_record 来了解记录是否存在。
您可能应该尝试完全摆脱警告,但如果这不可能,您可以在调用前加上@(即@dns_get_record(...)),然后使用您可以获得的任何信息来确定警告是否发生或不。
如果dns_get_record()
失败,它应该返回FALSE
,因此您可以使用@
抑制警告,然后检查返回值。
尝试检查它是否返回一些布尔值,然后您可以简单地将其作为条件。 我在 oci_execute(...) 遇到了这个问题,它使用我的唯一键返回了一些违规。
ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
从 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
不确定通知是否被捕获(可能不会),但您可以通过更多地考虑您想要做什么来解决像这样的示例。
构建器模式和选项模式都为此提供了解决方案,在调用站点之前(可以是私有函数或在有效性检查之后),您可以抛出仅归因于您的代码的真正自定义异常。 这将使内置函数的使用变得非常安全。
另一种好的做法是使用debug_backtrace
和DEBUG_BACKTRACE_IGNORE_ARGS
getTrace
getTraceAsString
方法,以便保留一些上下文。
文件夹结构
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
自 PHP7 以来,您可以捕获大多数错误和警告,如下所示:
try {
whatever();
} catch (Throwable $e) {
}
更多: https ://www.php.net/manual/en/language.errors.php7.php
我只建议在直接操作时使用 @ 来抑制警告(例如 $prop = @($high/($width - $depth)); 跳过除以零警告)。 但是在大多数情况下,最好处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.