Чи можу я спробувати / взяти попередження?


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, не знайдено записів для імені хоста.

Яка найкраща практика тут?


1
stackoverflow.com/questions/136899/… - це гарна дискусія щодо таких речей.
Меж

нижче була відповідь, яку видалили? чи власником, чи кимось?
користувач121196


@ 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
чи доцільно встановити власний обробник помилок перед викликом функції, а потім відновити_error_handler, коли перевірка помилок виконана?
користувач121196

2
це буде безпечно для потоків, якщо є багато одночасних запитів, і кожен запит робить 1.set_error_handler (). 2.doit 3.restore_error_handler?
користувач121196

4
Дякую; це допомагає. (І кажуть, що PHP - це не катастрофа.)
Аарон Міллер

2
+1, щоб уникнути використання @ для придушення помилок. E_WARNING - це фактично не фатальна помилка. Загалом, ви завжди повинні намагатися правильно обробляти помилки. Якщо ваша програма вимагає використання set_error_handler, зробіть це. Зазвичай доцільно реєструвати помилки та відключати показ їх у виробничому середовищі. Перевіривши журнали, ви зможете побачити, де вносити зміни у ваше середовище розробки. Занадто багато випадків, коли я бачив @ fopen / @ від’єднання, і цікаво, чому розробник не виконував перевірки, щоб уникнути помилок або не впоратися з помилкою за допомогою set_error_handler.
fyrye

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можна використовувати анонімні замість рядка з оголошенням функції
vp_arth

Дякую, але як я можу видалити обробку помилок після критичного блоку?
Євген Афанасьєв

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:~$ 

Цього разу ми можемо зрозуміти, чому це не вдалося.


5

Я хотів спробувати / зняти попередження, але в той же час вести звичайний журнал попередження / помилки (наприклад, увійти /var/log/apache2/error.log); за які обробник повинен повернутись false. Однак, оскільки заява "кинути нове ..." в основному перериває виконання, слід виконувати трюк "перетворити функцію", який також обговорювався в:

Чи є статичний спосіб викинути виняток у 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 ... Я все одно зберігаю це в опублікованому вигляді, хоча, як я ще не бачив такої поведінки документально підтвердженою в іншому місці.


4

Напевно, ви повинні спробувати позбутися попередження повністю, але якщо це неможливо, ви можете передозвонити виклик за допомогою @ (тобто @dns_get_record (...)), а потім використовувати будь-яку інформацію, яку ви зможете отримати, щоб з'ясувати, чи сталося попередження чи ні.


4

Зазвичай ви ніколи не використовуєте @, якщо це не єдине рішення. У цьому конкретному випадку функцію dns_check_record слід використовувати спочатку, щоб дізнатися, чи існує запис.


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

2

Якщо dns_get_record()не вдасться, він повинен повернутися FALSE, тому ви можете придушити попередження, @а потім перевірити значення повернення.


0

спробуйте перевірити, чи повертає воно якесь булеве значення, тоді ви можете просто поставити його як умову. Я зіткнувся з цим із oci_execute (...), який повертав якесь порушення моїми унікальними ключами.

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

0

ПапкаСтруктура

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 - ($ width - $ глибина)); Однак у більшості випадків краще впоратися.


2
Це один раз, коли ви точно не хочете використовувати @ - у вас є контроль над операцією і ви можете перевірити, чи це ділення на нуль чи ні, перш ніж робити це.
Eborbob
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.