Могу ли я попробовать / поймать предупреждение?

358

Мне нужно отловить некоторые предупреждения, выдаваемые родными функциями 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, никакие записи не найдены для имени хоста.

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

user121196
источник
1
stackoverflow.com/questions/136899/… - это хорошее обсуждение таких вещей.
Мез
был ответ ниже, который был удален? или владельцем или кем-то?
user121196
см. также: stackoverflow.com/questions/1087365
dreftymac
@ user121196: Да. Владельцем
Гонки легкости на орбите

Ответы:

373

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

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

Филипп Гербер
источник
3
Желательно ли установить свой собственный обработчик ошибок непосредственно перед вызовом функции, а затем восстановить_работающий_катайщик после проверки ошибок?
user121196
2
будет ли это потокобезопасным, если есть много одновременных запросов, и каждый запрос выполняет 1.set_error_handler (). 2.doit 3.restore_error_handler?
user121196
4
Спасибо; это помогает. (И они говорят, что PHP не беда.)
Аарон Миллер
2
+1 во избежание использования @ для подавления ошибок. E_WARNING на самом деле не фатальная ошибка. В общем, вы всегда должны стараться правильно обрабатывать ошибки. Если ваше приложение требует использования set_error_handler, сделайте это. Обычно рекомендуется регистрировать ошибки и отключать их отображение в производственной среде. После проверки журналов вы можете увидеть, где внести изменения в вашей среде разработки. Слишком много случаев, когда я видел @ fopen / @ unlink и удивляюсь, почему разработчик не выполнил проверки, чтобы избежать ошибок или обработать ошибку, используя set_error_handler.
фырье
5
Замечание о том, как превратить предупреждения в исключения: предупреждение не прервет ваше приложение - подойдет неперехваченное исключение!
Альваро Гонсалес
149

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

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

function warning_handler($errno, $errstr) { 
// do something
}
Роберт
источник
4
здесь также callableможно использовать анонимный
тип
Спасибо, но как я могу удалить обработчик ошибок после критического блока?
Евгений Афанасьев
3
Отлично! Просто trow new \Exception($errstr, $errno);внутри warning_handlerфункции. Спасибо.
Владимир Вуканац
Это лучший ответ здесь!
lewis4u
28

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

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

GuruBob
источник
5

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

sdaau
источник
4

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

rpjohnst
источник
4

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

florynth
источник
3

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

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

янтарный
источник
0

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

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
gborjal
источник
0

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
Джунед Ансари
источник
-2

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

tanovellino
источник
2
Это один раз, когда вы определенно не хотите использовать @ - у вас есть контроль над операцией и вы можете проверить, деление ли это на ноль или нет, прежде чем делать это.
Eborbob