Czy mogę spróbować / złapać ostrzeżenie?


358

Muszę złapać ostrzeżenia wyrzucane z niektórych rodzimych funkcji php, a następnie je obsłużyć.

Konkretnie:

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

Wyświetla ostrzeżenie, gdy zapytanie DNS nie powiedzie się.

try/ catchnie działa, ponieważ ostrzeżenie nie jest wyjątkiem.

Mam teraz 2 opcje:

  1. set_error_handler wygląda na przesadę, ponieważ muszę go używać do filtrowania każdego ostrzeżenia na stronie (czy to prawda?);

  2. Dostosuj raportowanie / wyświetlanie błędów, aby ostrzeżenia te nie były wyświetlane na ekranie, a następnie sprawdź wartość zwracaną; jeśli tak false, nie znaleziono rekordów dla nazwy hosta.

Jaka jest tutaj najlepsza praktyka?


1
stackoverflow.com/questions/136899/... to dobra dyskusja na takie tematy.
Poniedziałek

odpowiedź poniżej została usunięta? przez właściciela lub przez kogoś?
user121196,


@ user121196: Tak. Przez właściciela
Wyścigi lekkości na orbicie

Odpowiedzi:


373

Ustaw i przywróć moduł obsługi błędów

Jedną z możliwości jest ustawienie własnego modułu obsługi błędów przed wywołaniem i przywrócenie poprzedniego modułu obsługi błędów za pomocą restore_error_handler().

set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();

Możesz skorzystać z tego pomysłu i napisać program obsługi błędów wielokrotnego użytku, który rejestruje błędy.

set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();

Przekształcanie błędów w wyjątki

Możesz użyć set_error_handler()i ErrorExceptionklasy, aby zamienić wszystkie błędy php w wyjątki.

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) {
    // ...
}

Ważną rzeczą, na którą należy zwrócić uwagę podczas korzystania z własnego modułu obsługi błędów, jest to, że pominie ono error_reportingustawienie i przekaże wszystkie błędy (powiadomienia, ostrzeżenia itp.) Do modułu obsługi błędów. Możesz ustawić drugi argument, set_error_handler()aby zdefiniować typy błędów, które chcesz otrzymać, lub uzyskać dostęp do bieżącego ustawienia za pomocą ... = error_reporting()procedury obsługi błędów.

Tłumienie ostrzeżenia

Inną możliwością jest pomijanie połączenia z operatorem @ i sprawdzanie dns_get_record()późniejszej wartości zwrotu . Ale odradzałbym to, ponieważ błędy / ostrzeżenia są wywoływane w celu obsługi, a nie tłumienia.


3
czy wskazane jest ustawienie własnego modułu obsługi błędów bezpośrednio przed wywołaniem funkcji, a następnie przywrócenie modułu obsługi błędów po zakończeniu sprawdzania błędów?
user121196,

2
czy będzie to bezpieczne dla wątków, jeśli istnieje wiele równoczesnych żądań, a każde żądanie wykonuje 1.set_error_handler (). 2.doit 3.restore_error_handler?
user121196,

4
Dzięki; to pomaga. (I mówią, że PHP nie jest katastrofą.)
Aaron Miller

2
+1 za uniknięcie użycia @ do pomijania błędów. E_OSTRZEŻENIE jest tak naprawdę błędem niekrytycznym. Zasadniczo należy zawsze starać się odpowiednio obsługiwać błędy. Jeśli twoja aplikacja wymaga użycia set_error_handler, zrób to. Zazwyczaj zaleca się rejestrowanie błędów i wyłączanie ich wyświetlania w środowisku produkcyjnym. Po sprawdzeniu dzienników możesz zobaczyć, gdzie wprowadzić zmiany w swoim środowisku programistycznym. Zbyt wiele przypadków, w których widziałem @ fopen / @ unlink i zastanawiam się, dlaczego programista nie przeprowadził kontroli, aby uniknąć błędów lub obsłużyć błąd przy użyciu programu set_error_handler.
fyrye

5
Uwaga na temat przekształcania ostrzeżeń w wyjątki: ostrzeżenie nie przerwie działania Twojej aplikacji - zrobi to nieprzechwycony wyjątek!
Álvaro González

149

Rozwiązaniem, które naprawdę działa, było ustawienie prostej procedury obsługi błędów za pomocą E_WARNINGparametru, na przykład:

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

function warning_handler($errno, $errstr) { 
// do something
}

4
callablemożna tu również użyć anonimowego zamiast ciągu znaków z deklaracją funkcji
vp_arth

Dzięki, ale jak mogę usunąć moduł obsługi błędów po bloku krytycznym?
Jewgienij Afanasyjew

3
Świetny! Funkcja tylko w trow new \Exception($errstr, $errno);środku warning_handler. Dzięki.
Vladimir Vukanac

To najlepsza odpowiedź tutaj!
lewis4u

28

Bądź ostrożny z @operatorem - eliminując ostrzeżenia, eliminuje również błędy krytyczne. Spędziłem dużo czasu debugując problem w systemie, w którym ktoś napisał @mysql_query( '...' ). Problem polegał na tym, że obsługa mysql nie została załadowana do PHP i spowodowało to cichy błąd krytyczny. To będzie bezpieczne dla tych rzeczy, które są częścią jądra PHP, ale proszę go używać z rozwagą.

bob@mypc:~$ php -a
Interactive shell

php > echo @something(); // this will just silently die...

Brak dalszych wyników - powodzenia przy debugowaniu tego!

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

Tym razem możemy zobaczyć, dlaczego się nie udało.


5

Chciałem spróbować / złapać ostrzeżenie, ale jednocześnie zachować rejestrowanie zwykle ostrzeżenie / błąd (np /var/log/apache2/error.log); do którego przewodnik musi zwrócićfalse . Ponieważ jednak instrukcja „rzucaj nowe ...” zasadniczo przerywa wykonanie, należy wykonać sztuczkę „zawinąć w funkcję”, omówioną również w:

Czy istnieje statyczny sposób na zgłoszenie wyjątku w php

Lub w skrócie:

  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();
    // ...
  }

EDYCJA: po dokładniejszym sprawdzeniu okazuje się, że to nie działa: „ return false && throwErrorException ...” w zasadzie nie wyrzuci wyjątku i po prostu zaloguje się do dziennika błędów; usunięcie części „ false &&”, jak w „ return throwErrorException ...”, spowoduje, że zgłoszenie wyjątku zadziała, ale nie zaloguje się do dziennika error_log ... Nadal będę to publikował, ponieważ nie widziałem tego zachowania w innym miejscu.


4

Prawdopodobnie powinieneś spróbować całkowicie pozbyć się ostrzeżenia, ale jeśli nie jest to możliwe, możesz poprzedzić wywołanie za pomocą @ (tj. @Dns_get_record (...)), a następnie użyć wszelkich informacji, które możesz dowiedzieć się, czy ostrzeżenie się wydarzyło albo nie.


4

Normalnie nigdy nie powinieneś używać @, chyba że jest to jedyne rozwiązanie. W tym konkretnym przypadku należy najpierw użyć funkcji dns_check_record, aby dowiedzieć się, czy rekord istnieje.


3

Połączenie tych wierszy kodu wokół file_get_contents()wywołania zewnętrznego adresu URL pomogło mi obsługiwać ostrzeżenia takie jak „ nie udało się otworzyć strumienia: Przekroczono limit czasu połączenia ” znacznie lepiej:

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();

To rozwiązanie działa również w kontekście obiektowym. Możesz użyć go w funkcji:

public function myContentGetter($sUrl)
{
  ... code above ...
  return $iResult;
}

2

Jeśli się dns_get_record()nie powiedzie, powinien powrócić FALSE, aby można było wyłączyć ostrzeżenie za pomocą, @a następnie sprawdzić wartość zwracaną.


0

spróbuj sprawdzić, czy zwraca wartość logiczną, a następnie możesz po prostu ustawić ją jako warunek. Zetknąłem się z tym z oci_execute (...), który zwracał pewne naruszenia za pomocą moich unikalnych kluczy.

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

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");

wystarczy dołączyć powyższy plik do pliku skryptu w ten sposób

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

Zalecałbym używanie @ do tłumienia ostrzeżeń tylko wtedy, gdy jest to prosta operacja (np. $ Prop = @ ($ high / ($ width - $ depth)); aby pominąć podział przez zero ostrzeżeń). Jednak w większości przypadków lepiej sobie z tym poradzić.


2
Jest to jeden raz, kiedy zdecydowanie nie chcesz używać @ - masz kontrolę nad operacją i możesz sprawdzić, czy jest to dzielenie przez zero, czy nie, zanim to zrobisz.
Eborbob
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.