Могу ли я попробовать / поймать предупреждение? - PullRequest
323 голосов
/ 07 августа 2009

Мне нужно отлавливать некоторые предупреждения, выдаваемые родными функциями php, а затем обрабатывать их.

В частности:

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

Выдает предупреждение при сбое DNS-запроса.

try / catch не работает, поскольку предупреждение не является исключением.

Теперь у меня есть 2 варианта:

  1. set_error_handler кажется излишним, потому что я должен использовать его для фильтрации каждого предупреждения на странице (это правда?);

  2. Настройте отображение ошибок / отображение, чтобы эти предупреждения не отображались на экране, затем проверьте возвращаемое значение; если это false, для имени хоста не найдено никаких записей.

Какая лучшая практика здесь?

Ответы [ 10 ]

342 голосов
/ 07 августа 2009

Установить и восстановить обработчик ошибок

Одна из возможностей - установить собственный обработчик ошибок перед вызовом и позже восстановить предыдущий обработчик ошибок с помощью 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, array $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(). Но я бы посоветовал против этого , поскольку ошибки / предупреждения инициируются для обработки, а не для подавления.

136 голосов
/ 26 июня 2012

Решение, которое действительно работает, оказалось установкой простого обработчика ошибок с параметром E_WARNING, например:

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

function warning_handler($errno, $errstr) { 
// do something
}
27 голосов
/ 10 августа 2011

Будьте осторожны с оператором @ - хотя он подавляет предупреждения, он также подавляет фатальные ошибки. Я потратил много времени на отладку проблемы в системе, где кто-то написал @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:~$ 

На этот раз мы можем понять, почему это не удалось.

5 голосов
/ 27 декабря 2013

Я хотел попробовать / поймать предупреждение, но в то же время сохранить обычное предупреждение / запись в журнал ошибок (например, в /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 ... Однако я все равно буду держать это в курсе, поскольку я этого не делал видел это поведение, документированное в другом месте.

4 голосов
/ 04 января 2011

Обычно вы никогда не должны использовать @, если это не единственное решение. В этом конкретном случае сначала следует использовать функцию dns_check_record, чтобы узнать, существует ли запись.

4 голосов
/ 07 августа 2009

Возможно, вам следует попытаться полностью избавиться от предупреждения, но если это невозможно, вы можете добавить к вызову @ (то есть @dns_get_record (...)), а затем использовать любую информацию, которую сможете получить, чтобы выяснить, предупреждение произошло или нет.

3 голосов
/ 29 июня 2018

Объединение этих строк кода вокруг 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;
}
2 голосов
/ 07 августа 2009

Если dns_get_record() не удалось, он должен вернуть FALSE, поэтому вы можете подавить предупреждение с помощью @ и затем проверить возвращаемое значение.

0 голосов
/ 08 ноября 2017

попробуйте проверить, возвращает ли оно какое-либо логическое значение, тогда вы можете просто указать его как условие. Я столкнулся с этим с oci_execute (...), который возвращал некоторое нарушение с моими уникальными ключами.

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
0 голосов
/ 02 сентября 2016

Я бы рекомендовал использовать @ только для подавления предупреждений, когда это прямая операция (например, $ prop = @ ($ high / ($ width - $ глубина)); чтобы пропустить деление на ноль предупреждений). Однако в большинстве случаев лучше обращаться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...