Posso provare / ricevere un avviso?


358

Ho bisogno di catturare alcuni avvisi lanciati da alcune funzioni native di PHP e quindi gestirli.

In particolare:

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

Emette un avviso quando la query DNS non riesce.

try/ catchnon funziona perché un avviso non è un'eccezione.

Ora ho 2 opzioni:

  1. set_error_handler sembra eccessivo perché devo usarlo per filtrare tutti gli avvisi nella pagina (è vero?);

  2. Regola la segnalazione / visualizzazione degli errori in modo che questi avvisi non vengano ripetuti sullo schermo, quindi controlla il valore restituito; in tal caso false, non viene trovato alcun record per il nome host.

Qual è la migliore pratica qui?


1
stackoverflow.com/questions/136899/… è una buona discussione su cose come questa.
Mez,

c'era una risposta qui sotto che è stata cancellata? o dal proprietario o da qualcuno?
user121196


@ user121196: Sì. Dal proprietario.
Razze di leggerezza in orbita

Risposte:


373

Imposta e ripristina il gestore errori

Una possibilità è impostare il proprio gestore degli errori prima della chiamata e ripristinare il precedente gestore degli errori con restore_error_handler().

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

Potresti basarti su questa idea e scrivere un gestore di errori riutilizzabile che registra gli errori per te.

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

Trasformare gli errori in eccezioni

Puoi usare set_error_handler()e la ErrorExceptionclasse per trasformare tutti gli errori php in eccezioni.

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

La cosa importante da notare quando si utilizza il proprio gestore degli errori è che ignorerà l' error_reportingimpostazione e passerà tutti gli errori (avvisi, avvertenze, ecc.) Al proprio gestore degli errori. È possibile impostare un secondo argomento set_error_handler()per definire quali tipi di errore si desidera ricevere o accedere all'impostazione corrente utilizzando ... = error_reporting()all'interno del gestore errori.

Sopprimendo l'avvertimento

Un'altra possibilità è quella di sopprimere la chiamata con l'operatore @ e verificare il valore di ritorno di dns_get_record()successivamente. Ma sconsiglio questo perché gli errori / avvertimenti vengono attivati ​​per essere gestiti, non per essere soppressi.


3
è consigliabile impostare il mio gestore degli errori proprio prima della chiamata della funzione, quindi restore_error_handler al termine del controllo degli errori?
user121196

2
sarà sicuro per i thread se ci sono molte richieste simultanee e ogni richiesta fa 1.set_error_handler (). 2.doit 3.restore_error_handler?
user121196

4
Grazie; questo aiuta. (E dicono che PHP non è un disastro.)
Aaron Miller,

2
+1 per evitare di usare @ per sopprimere gli errori. E_WARNING è in realtà un errore non fatale. In generale, dovresti sempre provare a gestire gli errori in modo appropriato. Se la tua applicazione richiede l'uso di set_error_handler, fallo. Di solito è consigliabile registrare errori e disabilitarne la visualizzazione in un ambiente di produzione. Dopo aver controllato i log, è possibile vedere dove apportare modifiche nel proprio ambiente di sviluppo. Troppi casi in cui ho visto @ fopen / @ unlink e mi chiedo perché lo sviluppatore non abbia eseguito controlli per evitare errori o gestire l'errore usando set_error_handler.
Fyrye,

5
Una nota sulla trasformazione degli avvisi in eccezioni: un avviso non interromperà la tua app, ma un'eccezione non rilevata lo farà!
Álvaro González,

149

La soluzione che funziona davvero è stata l'impostazione di un semplice gestore di errori con E_WARNINGparametro, in questo modo:

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

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

4
anche anonimo callablepuò essere usato qui invece di stringa con dichiarazione di funzione
vp_arth

Grazie, ma come posso rimuovere il gestore degli errori dopo il blocco critico?
Yevgeniy Afanasyev il

3
Eccellente! Solo trow new \Exception($errstr, $errno);all'interno della warning_handlerfunzione. Grazie.
Vladimir Vukanac,

Questa è la migliore risposta qui!
lewis4u,

28

Fai attenzione con l' @operatore : mentre sopprime gli avvisi, elimina anche gli errori fatali. Ho trascorso molto tempo a eseguire il debug di un problema in un sistema in cui qualcuno aveva scritto @mysql_query( '...' )e il problema era che il supporto mysql non veniva caricato in PHP e generava un errore fatale silenzioso. Sarà sicuro per quelle cose che fanno parte del core di PHP ma per favore usalo con cura.

bob@mypc:~$ php -a
Interactive shell

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

Nessun ulteriore output - buona fortuna debug questo!

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

Questa volta possiamo vedere perché non è riuscito.


5

Volevo provare / catturare un avviso, ma allo stesso tempo mantenere il solito avviso / registrazione degli errori (ad es /var/log/apache2/error.log. per cui il gestore deve restituire false. Tuttavia, poiché l'istruzione "lancio nuovo ..." sostanzialmente interrompe l'esecuzione, si deve fare il trucco "avvolgi in funzione", anch'esso discusso in:

Esiste un modo statico per generare eccezioni in php

Oppure, in breve:

  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: dopo un'attenta ispezione, si scopre che non funziona: " return false && throwErrorException ..." fondamentalmente non genererà l'eccezione e accederà semplicemente al registro degli errori; la rimozione della " false &&" parte, come in " return throwErrorException ...", farà funzionare l'eccezione, ma non accederà al log degli errori ... Tuttavia continuerei a tenerlo pubblicato, poiché non ho visto questo comportamento documentato altrove.


4

Probabilmente dovresti provare a eliminare completamente l'avviso, ma se ciò non è possibile, puoi anteporre la chiamata con @ (ovvero @dns_get_record (...)) e quindi utilizzare tutte le informazioni che puoi ottenere per capire se l'avviso è avvenuto o no.


4

Normalmente non dovresti mai usare @ a meno che questa non sia l'unica soluzione. In quel caso specifico la funzione dns_check_record dovrebbe essere usata per prima cosa per sapere se il record esiste.


3

La combinazione di queste righe di codice attorno a una file_get_contents()chiamata a un URL esterno mi ha aiutato a gestire avvisi come " impossibile aprire il flusso: Timeout della connessione " molto meglio:

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

Questa soluzione funziona anche nel contesto dell'oggetto. Puoi usarlo in una funzione:

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

2

Se dns_get_record()fallisce, dovrebbe restituire FALSE, quindi puoi sopprimere l'avviso con @e quindi controllare il valore restituito.


0

prova a verificare se restituisce un valore booleano, quindi puoi semplicemente inserirlo come condizione. Ho riscontrato questo con oci_execute (...) che stava restituendo una violazione con le mie chiavi univoche.

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

includi semplicemente il file sopra nel tuo file di script come questo

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

Consiglierei di usare @ solo per sopprimere gli avvisi quando si tratta di un'operazione semplice (ad esempio $ prop = @ ($ high / ($ larghezza - $ profondità)); per saltare la divisione per zero avvertimenti). Tuttavia, nella maggior parte dei casi è meglio gestirlo.


2
Questa è una volta che non vuoi assolutamente usare @: hai il controllo sull'operazione e puoi verificare se è una divisione per zero o meno prima di farlo.
Eborbob,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.