Kann ich versuchen, eine Warnung abzufangen?


358

Ich muss einige Warnungen abfangen, die von einigen nativen PHP-Funktionen ausgelöst werden, und sie dann verarbeiten.

Speziell:

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

Es wird eine Warnung ausgegeben, wenn die DNS-Abfrage fehlschlägt.

try/ catchfunktioniert nicht, weil eine Warnung keine Ausnahme ist.

Ich habe jetzt 2 Möglichkeiten:

  1. set_error_handler scheint übertrieben, weil ich es verwenden muss, um jede Warnung auf der Seite zu filtern (ist das wahr?);

  2. Passen Sie die Fehlerberichterstattung / -anzeige so an, dass diese Warnungen nicht auf dem Bildschirm angezeigt werden, und überprüfen Sie dann den Rückgabewert. Wenn dies der falseFall ist, wurden keine Datensätze für den Hostnamen gefunden.

Was ist hier die beste Vorgehensweise?


1
stackoverflow.com/questions/136899/… ist eine gute Diskussion über solche Dinge.
Mez

gab es eine Antwort unten, die gelöscht wurde? entweder vom Besitzer oder von jemandem?
user121196


@ user121196: Ja. Vom Eigentümer.
Leichtigkeitsrennen im Orbit

Antworten:


373

Fehlerbehandlungsroutine festlegen und wiederherstellen

Eine Möglichkeit besteht darin, vor dem Aufruf einen eigenen Fehlerbehandler festzulegen und den vorherigen Fehlerbehandler später mit wiederherzustellen restore_error_handler().

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

Sie können auf dieser Idee aufbauen und einen wiederverwendbaren Fehlerbehandler schreiben, der die Fehler für Sie protokolliert.

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

Fehler in Ausnahmen verwandeln

Sie können set_error_handler()und die ErrorExceptionKlasse verwenden, um alle PHP-Fehler in Ausnahmen umzuwandeln.

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

Wenn Sie Ihren eigenen Fehlerbehandler verwenden, ist es wichtig zu beachten, dass er die error_reportingEinstellung umgeht und alle Fehler (Hinweise, Warnungen usw.) an Ihren Fehlerbehandler weiterleitet. Sie können ein zweites Argument set_error_handler()festlegen, um zu definieren, welche Fehlertypen Sie empfangen möchten, oder über ... = error_reporting()die Fehlerbehandlung auf die aktuelle Einstellung zugreifen .

Warnung unterdrücken

Eine andere Möglichkeit besteht darin, den Aufruf mit dem Operator @ zu unterdrücken und anschließend den Rückgabewert von zu überprüfen dns_get_record(). Ich würde jedoch davon abraten, da Fehler / Warnungen ausgelöst werden, um behandelt und nicht unterdrückt zu werden.


3
Ist es ratsam, meinen eigenen Fehlerbehandler direkt vor dem Funktionsaufruf festzulegen und dann den Wiederherstellungsfehler-Handler wiederherzustellen, wenn die Fehlerprüfung abgeschlossen ist?
user121196

2
Ist dies threadsicher, wenn viele Anforderungen gleichzeitig ausgeführt werden und jede Anforderung 1.set_error_handler () ausführt? 2.doit 3.restore_error_handler?
user121196

4
Vielen Dank; das hilft. (Und sie sagen, PHP ist keine Katastrophe.)
Aaron Miller

2
+1 zur Vermeidung der Verwendung von @ zur Unterdrückung von Fehlern. E_WARNING ist eigentlich ein nicht schwerwiegender Fehler. Im Allgemeinen sollten Sie immer versuchen, Fehler angemessen zu behandeln. Wenn Ihre Anwendung die Verwendung von set_error_handler erfordert, tun Sie dies. In der Regel ist es ratsam, Fehler zu protokollieren und deren Anzeige in einer Produktionsumgebung zu deaktivieren. Wenn Sie die Protokolle überprüfen, können Sie sehen, wo Sie Änderungen in Ihrer Entwicklungsumgebung vornehmen müssen. Zu viele Fälle, in denen ich @ fopen / @ unlink gesehen habe und mich gefragt habe, warum der Entwickler keine Überprüfungen durchgeführt hat, um die Fehler zu vermeiden oder den Fehler mit set_error_handler zu behandeln.
Fyrye

5
Ein Hinweis zum Verwandeln von Warnungen in Ausnahmen: Eine Warnung bricht Ihre App nicht ab - eine nicht erfasste Ausnahme reicht aus!
Álvaro González

149

Es stellte sich heraus, dass die Lösung, die wirklich funktioniert, darin besteht, einen einfachen Fehlerbehandler mit E_WARNINGParametern festzulegen:

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

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

4
Auch anonym callablekann hier anstelle von String mit Funktionsdeklaration verwendet werden
vp_arth

Danke, aber wie kann ich den Fehlerbehandler nach dem kritischen Block entfernen?
Jewgenij Afanasjew

3
Ausgezeichnet! Nur trow new \Exception($errstr, $errno);innerhalb der warning_handlerFunktion. Vielen Dank.
Vladimir Vukanac

Dies ist die beste Antwort hier!
Lewis4u

28

Seien Sie vorsichtig mit dem @Bediener - während er Warnungen unterdrückt, werden auch schwerwiegende Fehler unterdrückt. Ich habe viel Zeit damit verbracht, ein Problem in einem System zu debuggen, in dem jemand geschrieben hatte, @mysql_query( '...' )und das Problem war, dass die MySQL-Unterstützung nicht in PHP geladen wurde und einen stillen schwerwiegenden Fehler verursachte. Es ist sicher für die Dinge, die Teil des PHP-Kerns sind, aber bitte verwenden Sie es mit Vorsicht.

bob@mypc:~$ php -a
Interactive shell

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

Keine weitere Ausgabe - viel Glück beim Debuggen!

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

Diesmal können wir sehen, warum es fehlgeschlagen ist.


5

Ich wollte versuchen, eine Warnung abzufangen, aber gleichzeitig die übliche Warnung / Fehlerprotokollierung beibehalten (z. B. in /var/log/apache2/error.log). für die der Handler zurückkehren muss false. Da jedoch die Anweisung "throw new ..." die Ausführung grundsätzlich unterbricht, muss der Trick "Wrap in Function" ausgeführt werden, der auch in:

Gibt es eine statische Möglichkeit, eine Ausnahme in PHP auszulösen?

Oder kurz:

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

BEARBEITEN: Bei näherer Betrachtung stellt sich heraus, dass es nicht funktioniert: Das " return false && throwErrorException ..." löst im Grunde genommen keine Ausnahme aus und meldet sich einfach im Fehlerprotokoll an. Wenn Sie den false &&Teil " " wie in " return throwErrorException ..." entfernen , funktioniert das Auslösen der Ausnahme, aber dann wird das error_log nicht angemeldet. Ich würde dies jedoch weiterhin auf dem Laufenden halten, da ich dieses Verhalten an keiner anderen Stelle dokumentiert habe.


4

Sie sollten wahrscheinlich versuchen, die Warnung vollständig zu entfernen. Wenn dies jedoch nicht möglich ist, können Sie dem Anruf @ (dh @dns_get_record (...)) voranstellen und dann alle Informationen verwenden, um herauszufinden, ob die Warnung aufgetreten ist oder nicht.


4

Normalerweise sollten Sie @ niemals verwenden, es sei denn, dies ist die einzige Lösung. In diesem speziellen Fall sollte zuerst die Funktion dns_check_record verwendet werden, um festzustellen, ob der Datensatz vorhanden ist.


3

Das Kombinieren dieser Codezeilen um einen file_get_contents()Aufruf einer externen URL hat mir geholfen, Warnungen wie " Fehler beim Öffnen des Streams: Zeitüberschreitung bei Verbindung " viel besser zu verarbeiten:

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

Diese Lösung funktioniert auch im Objektkontext. Sie könnten es in einer Funktion verwenden:

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

2

Wenn dies dns_get_record()fehlschlägt, sollte es zurückgegeben werden FALSE, damit Sie die Warnung mit unterdrücken @und dann den Rückgabewert überprüfen können.


0

Versuchen Sie zu überprüfen, ob ein boolescher Wert zurückgegeben wird. Dann können Sie ihn einfach als Bedingung festlegen. Ich bin auf das oci_execute (...) gestoßen, das mit meinen eindeutigen Schlüsseln einen Verstoß zurückgegeben hat.

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

0

Ordnerstruktur

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

Fügen Sie einfach die obige Datei wie folgt in Ihre Skriptdatei ein

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

Ich würde nur empfehlen, @ zu verwenden, um Warnungen zu unterdrücken, wenn es sich um eine einfache Operation handelt (z. B. $ prop = @ ($ high / ($ width - $ depth)); um die Division durch Nullwarnungen zu überspringen). In den meisten Fällen ist es jedoch besser zu handhaben.


2
Dies ist ein Mal, wenn Sie @ definitiv nicht verwenden möchten - Sie haben die Kontrolle über die Operation und können überprüfen, ob es sich um eine Division durch Null handelt oder nicht, bevor Sie dies tun.
Eborbob
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.