Benutzerdefinierte Ausnahmemeldungen: Best Practices

Lesezeit: 8 Minuten

Benutzer-Avatar
Timoxley

Sie fragen sich, wie viel Aufwand ich betreiben sollte, um beim Erstellen von Ausnahmemeldungen nützliche Debugging-Informationen zu erzwingen, oder sollte ich einfach darauf vertrauen, dass der Benutzer die richtigen Informationen bereitstellt, oder das Sammeln von Informationen an einen Ausnahmehandler verschieben?

Ich sehe viele Leute, die ihre Ausnahmen machen wie:

throw new RuntimeException('MyObject is not an array')

oder die Standardausnahmen mit benutzerdefinierten Ausnahmen erweitern, die nicht viel tun, aber den Namen der Ausnahme ändern:

throw new WrongTypeException('MyObject is not an array')

Aber dies liefert nicht viele Debugging-Informationen … und erzwingt keinerlei Formatierung mit der Fehlermeldung. Sie könnten also mit genau demselben Fehler enden, der zwei verschiedene Fehlermeldungen erzeugt … zB “Datenbankverbindung fehlgeschlagen” vs “Konnte keine Verbindung zu db herstellen”

Sicher, wenn es nach oben sprudelt, wird der Stack-Trace gedruckt, was nützlich ist, aber es sagt mir nicht immer alles, was ich wissen muss, und normalerweise muss ich anfangen, var_dump () -Anweisungen abzuschießen, um es zu entdecken was schief gelaufen ist und wo … obwohl dies mit einem anständigen Ausnahmehandler etwas ausgeglichen werden könnte.

Ich fange an, über etwas wie den folgenden Code nachzudenken, wo ich benötigen der Werfer der Ausnahme, um die erforderlichen Argumente bereitzustellen, um die richtige Fehlermeldung zu erzeugen. Ich denke, dass dies der Weg sein könnte:

  • Es muss ein Mindestmaß an nützlichen Informationen bereitgestellt werden
  • Erzeugt etwas konsistente Fehlermeldungen
  • Vorlagen für Ausnahmemeldungen an einem Ort (Ausnahmeklassen), sodass die Meldungen einfacher aktualisiert werden können …

Aber ich sehe den Nachteil darin, dass sie schwieriger zu verwenden sind (erfordert, dass Sie die Ausnahmedefinition nachschlagen) und daher andere Programmierer davon abhalten könnten, bereitgestellte Ausnahmen zu verwenden …

Ich hätte gerne einen Kommentar zu dieser Idee und Best Practices für ein konsistentes, flexibles Rahmenwerk für Ausnahmemeldungen.

/**
* @package MyExceptions
* MyWrongTypeException occurs when an object or 
* datastructure is of the incorrect datatype.
* Program defensively!
* @param $objectName string name of object, eg "\$myObject"
* @param $object object object of the wrong type
* @param $expect string expected type of object eg 'integer'
* @param $message any additional human readable info.
* @param $code error code.
* @return Informative exception error message.
* @author secoif
*/
class MyWrongTypeException extends RuntimeException {
    public function __construct($objectName, $object, $expected, $message="", $code = 0) {
        $receivedType = gettype($object) 
        $message = "Wrong Type: $objectName. Expected $expected, received $receivedType";
        debug_dump($message, $object);
        return parent::__construct($message, $code);
    }
}

….

/**
 * If we are in debug mode, append the var_dump of $object to $message
 */
function debug_dump(&$message, &$object) {
     if (App::get_mode() == 'debug') {
         ob_start();
         var_dump($object);
         $message = $message . "Debug Info: " . ob_get_clean();
    }
}

Dann verwendet wie:

// Hypothetical, supposed to return an array of user objects
$users = get_users(); // but instead returns the string 'bad'
// Ideally the $users model object would provide a validate() but for the sake
// of the example
if (is_array($users)) {
  throw new MyWrongTypeException('$users', $users, 'array')
  // returns 
  //"Wrong Type: $users. Expected array, received string
}

und wir könnten so etwas wie ein nl2br in einem benutzerdefinierten Ausnahmehandler machen, um die Dinge für die HTML-Ausgabe angenehm zu machen.

Gelesen:
http://msdn.microsoft.com/en-us/library/cc511859.aspx#

Und so etwas wird nicht erwähnt, also ist es vielleicht eine schlechte Idee …

  • Ziemlich sicher, dass der Konstruktor hier übereinstimmen müsste RuntimeException::__construct()

    – Jakob Thomason

    31. Mai 2015 um 3:13 Uhr

Benutzer-Avatar
ShuggyCoUk

Ich empfehle dringend die Beratung auf Krzysztofs Blog und möchte anmerken, dass Sie in Ihrem Fall anscheinend versuchen, mit dem umzugehen, was er Nutzungsfehler nennt.

In diesem Fall ist kein neuer Typ erforderlich, um dies anzuzeigen, sondern eine bessere Fehlermeldung über die Ursache. Als solche Hilfsfunktion entweder:

  1. Generieren Sie die Textzeichenfolge, die in die Ausnahme eingefügt werden soll
  2. Generieren Sie die gesamte Ausnahme und Nachricht

Ist das, was erforderlich ist.

Ansatz 1 ist klarer, kann aber zu einer etwas ausführlicheren Verwendung führen, Ansatz 2 ist das Gegenteil und tauscht eine knappere Syntax gegen weniger Klarheit ein.

Beachten Sie, dass die Funktionen extrem sicher sein müssen (sie sollten niemals selbst eine unabhängige Ausnahme verursachen) und nicht die Bereitstellung von Daten erzwingen, die bei bestimmten vernünftigen Verwendungen optional sind.

Durch die Verwendung eines dieser Ansätze erleichtern Sie es, die Fehlermeldung später bei Bedarf zu internationalisieren.

Ein Stack-Trace gibt Ihnen zumindest die Funktion und möglicherweise die Zeilennummer an, daher sollten Sie sich darauf konzentrieren, Informationen bereitzustellen, die daraus nicht leicht zu ermitteln sind.

  • Ja, jetzt sehe ich. Ich mache viele Verwendungsfehler und Plausibilitätsprüfungen, obwohl die Plausibilitätsprüfungen wahrscheinlich auf assert()-Aufrufe verschoben werden sollten php.net/manual/en/function.assert.php.

    – Timoxley

    10. März 2009 um 3:55 Uhr

  • Krzysztofs Blog schlägt vor, vorhandene Typen wiederzuverwenden. dh. FileNotFoundException, NullArgumentException usw. PHP hat jedoch nur einen Ausnahmetyp – Exception. Es kann erweitert werden, aber das ist das einzige, das es standardmäßig enthält, und idk, dass das an und für sich besonders nützlich ist …

    – neubert

    15. April 2015 um 16:00 Uhr

  • Tipp: Die Standard PHP Library (SPL) bietet eine große Anzahl eingebauter Ausnahmen. php.net/manual/en/spl.exceptions.php

    – GDmac

    5. Mai 2020 um 21:05 Uhr

Ich werde nicht von den Ratschlägen zu Krzysztofs Blog ablenken, aber hier ist eine kinderleichte Möglichkeit, benutzerdefinierte Ausnahmen zu erstellen.

Beispiel:

<?php
   require_once "CustomException.php";
   class SqlProxyException extends CustomException {}

   throw new SqlProxyException($errorMsg, mysql_errno());     
?>

Der Code dahinter (den ich mir irgendwo ausgeliehen habe, entschuldige denjenigen, der das war)

<?php

interface IException
{
    /* Protected methods inherited from Exception class */
    public function getMessage();                 // Exception message
    public function getCode();                    // User-defined Exception code
    public function getFile();                    // Source filename
    public function getLine();                    // Source line
    public function getTrace();                   // An array of the backtrace()
    public function getTraceAsString();           // Formated string of trace

    /* Overrideable methods inherited from Exception class */
    public function __toString();                 // formated string for display
    public function __construct($message = null, $code = 0);
}

abstract class CustomException extends Exception implements IException
{
    protected $message="Unknown exception";     // Exception message
    private   $string;                            // Unknown
    protected $code    = 0;                       // User-defined exception code
    protected $file;                              // Source filename of exception
    protected $line;                              // Source line of exception
    private   $trace;                             // Unknown

    public function __construct($message = null, $code = 0)
    {
        if (!$message) {
            throw new $this('Unknown '. get_class($this));
        }
        parent::__construct($message, $code);
    }

    public function __toString()
    {
        return get_class($this) . " '{$this->message}' in {$this->file}({$this->line})\n"
                                . "{$this->getTraceAsString()}";
    }
}

  • +1 für den ersten Ort, an dem ich gefunden habe, der mir tatsächlich gesagt hat, wie man eine PHP-Ausnahme definiert und wie man sie auslöst

    – Brian Underwood

    17. Dezember 2015 um 3:34 Uhr

  • Meine große Frage hier ist, wie zum Teufel hast du überschrieben LETZTE öffentliche Funktion getCode()?

    – Alex Bärker

    12. Mai 2016 um 20:53 Uhr

  • Guter Gott, dieser Code wurde ursprünglich vor 7 Jahren geschrieben (ich habe gerade herausgefunden, wer ihn geschrieben hat). Es ist immer noch der zweitoberste Kommentar auf der Ausnahme-Manpage von php.net. Ich habe PHP 5.3 ausgeführt, als ich es kopiert habe, und ich verwende jetzt PHP 5.4. Was eine endgültige Methode in PHP 8 sein kann oder nicht (Zukunftssicherheit meines Kommentars dort), ist mir egal 🙂 php.net/manual/en/language.exceptions.php#91159

    – Orwellophil

    8. Juni 2016 um 9:22 Uhr

  • Wer auch immer diese Antwort ablehnen möchte, tun Sie dies bitte nicht mit einer Erklärung. Ich habe es gerade mit PHP 7.0.6 und PHP 5.4,43 ohne Probleme überprüft.

    – Orwellophil

    8. Juni 2016 um 10:00 Uhr

Benutzer-Avatar
John Saunders

Sehen So entwerfen Sie Ausnahmehierarchien auf dem Blog von Krzysztof Cwalina, einem Co-Autor von “Framework Design Guidelines”.

  • Es gibt einige gute Punkte darin, aber ich bin nicht 100 % überzeugt. Ich mag dieses Konzept:

    – Timoxley

    10. März 2009 um 7:12 Uhr

  • “Ausnahmen, die in der Datenzugriffsschicht auftreten, werden protokolliert und dann in eine andere Ausnahme eingeschlossen, die der aufrufenden Schicht aussagekräftigere Informationen liefert …

    – Timoxley

    10. März 2009 um 7:13 Uhr

  • … . Innerhalb der Geschäftskomponentenschicht werden die Ausnahmen protokolliert, bevor sie weitergegeben werden. Alle Ausnahmen, die in der Geschäftskomponentenschicht auftreten und vertrauliche Informationen enthalten, werden durch Ausnahmen ersetzt, die diese Informationen nicht mehr enthalten. …

    – Timoxley

    10. März 2009 um 7:14 Uhr

  • … Diese werden an die Benutzerschnittstellenschicht (UI) gesendet und dem Benutzer angezeigt.” von msdn.microsoft.com/en-us/library/cc511859.aspx# Dies lässt mich über das Entwurfsmuster der Verantwortungskette nachdenken: en.wikipedia.org/wiki/Chain_of_responsibility_pattern#PHP

    – Timoxley

    10. März 2009 um 7:16 Uhr

  • @mickmackusa 2009 nicht so sehr.

    – John Saunders

    3. Juni 2020 um 10:13 Uhr

Vertrauen Sie niemals einem Benutzer, dass er „das Richtige tut“, und fügen Sie Informationen zum Debuggen hinzu. Wenn Sie Informationen wünschen, müssen Sie sie selbst sammeln und an einem Ort aufbewahren, an dem sie zugänglich sind.

Wie bereits erwähnt, wenn es schwierig ist, etwas zu tun, werden die Benutzer es vermeiden, also verlassen Sie sich auch hier nicht auf ihren guten Willen und ihr Wissen darüber, was sie senden müssen.

Dieses Denken impliziert eine Methode, mit der Sie die Informationen sammeln und protokollieren, was die Verwendung von var_dump() irgendwo impliziert.

Außerdem ist, wie von Mark Harrison gesagt, eine Schaltfläche, die es einfach macht, irgendwo eine Fehlermeldung zu senden, fantastisch für Sie und die Benutzer. Es macht es ihnen leicht, einen Fehler zu melden. Sie (als Empfänger) erhalten viele Duplikate, aber doppelte Informationen sind besser als keine Informationen.

Egal, wie viele Details Sie hinzufügen, seien Sie sicher, und beides

  • Machen Sie es einfach, das Ganze auszuschneiden und einzufügen, oder
  • haben eine Schaltfläche, die den Fehler für sie meldet

  • …obwohl dies eher eine abschließende Maßnahme als ein Architekturproblem ist

    – Timoxley

    10. März 2009 um 3:41 Uhr

  • …obwohl dies eher eine abschließende Maßnahme als ein Architekturproblem ist

    – Timoxley

    10. März 2009 um 3:41 Uhr

1345990cookie-checkBenutzerdefinierte Ausnahmemeldungen: Best Practices

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy