Могу ли я попытаться/поймать предупреждение?
Мне нужно уловить некоторые предупреждения, которые выбрасываются из некоторых собственных функций php, а затем обрабатывать их.
В частности:
array dns_get_record ( string $hostname [, int $type= DNS_ANY [, array &$authns [, array &$addtl ]]] )
Он выдает предупреждение при сбое DNS-запроса.
try
/catch
не работает, потому что предупреждение не является исключением.
Теперь у меня есть 2 варианта:
-
set_error_handler
кажется излишним, потому что я должен использовать его для фильтрации каждого предупреждения на странице (это правда?);
-
Отрегулируйте отчет/отображение ошибок, чтобы эти предупреждения не отображались на экране, а затем проверяли возвращаемое значение; если он false
, для имени хоста не найдено записей.
Какая самая лучшая практика здесь?
Ответы
Ответ 1
Установить и восстановить обработчик ошибок
Одна возможность состоит в том, чтобы установить свой собственный обработчик ошибок перед вызовом и восстановить предыдущий обработчик ошибок позже с помощью 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, $errcontext) {
// 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()
. Но я бы посоветовал против этого, так как ошибки/предупреждения запускаются для обработки, а не для подавления.
Ответ 2
Решение, которое действительно работает, оказалось простым установщиком ошибок с параметром E_WARNING
, например:
set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();
function warning_handler($errno, $errstr) {
// do something
}
Ответ 3
Будьте осторожны с оператором @
- пока он подавляет предупреждения, он также подавляет фатальные ошибки. Я потратил много времени на отладку проблемы в системе, где кто-то написал @mysql_query( '...' )
, и проблема заключалась в том, что поддержка mysql не была загружена в PHP, и это заставило молчать фатальную ошибку. Это будет безопасно для тех вещей, которые являются частью ядра PHP, но, пожалуйста, используйте его с осторожностью.
[email protected]:~$ php -a
Interactive shell
php > echo @something(); // this will just silently die...
Отсутствие дальнейшего вывода - удача отладки этого!
[email protected]:~$ 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
[email protected]:~$
На этот раз мы можем понять, почему это не удалось.
Ответ 4
Я хотел попробовать/поймать предупреждение, но в то же время сохранить обычное предупреждение/журнал ошибок (например, в /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();
// ...
}
EDIT: после более тщательного осмотра выясняется, что он не работает: "return false && throwErrorException ...
", в основном, не будет генерировать исключение и просто зарегистрировать журнал ошибок; удаление части "false &&
", как в "return throwErrorException ...
", сделает работу по выбросу исключений, но затем не войдет в файл error_log... Я все равно сохранил бы это сообщение, хотя у меня не было видел это поведение, задокументированное в других местах.
Ответ 5
Вероятно, вы должны попытаться полностью избавиться от предупреждения, но если это невозможно, вы можете добавить вызов с помощью @(т.е. @dns_get_record (...)), а затем использовать любую информацию, которую вы можете выяснить, если предупреждение произошло или нет.
Ответ 6
Нормально, вы никогда не должны использовать @, если это не единственное решение. В этом конкретном случае функция dns_check_record должна быть первой, чтобы знать, существует ли запись.
Ответ 7
Объединение этих строк кода вокруг вызова file_get_contents()
на внешний URL помогло мне обработать предупреждения, такие как " не удалось открыть поток: соединение было вычеркнуто " намного лучше:
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;
}
Ответ 8
Если dns_get_record()
терпит неудачу, он должен вернуть FALSE
, поэтому вы можете подавить предупреждение с помощью @
, а затем проверить возвращаемое значение.
Ответ 9
попробуйте проверить, возвращает ли оно какое-то логическое значение, тогда вы можете просто поставить его как условие. Я столкнулся с этим с oci_execute (...), который возвращал некоторые нарушения с моими уникальными ключами.
ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
Ответ 10
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
Ответ 11
Я бы рекомендовал использовать @для подавления предупреждений при прямой работе (например, $prop = @($ high/($ width - $depth)), чтобы пропустить деление на нулевые предупреждения). Однако в большинстве случаев лучше справляться.