Die Flagge des Marasek

Dekostreifen

English

Aktuell Texte Der Comic Impressum Kalender Suche PHP-Klassen Container-Wizard main.s21

Kategorien

Buch
Computer
Computerspiele
Film
Geschichte
Gesellschaft
Idee
Kunst
Natur
Persönlich
Politik
Programmieren
Religion & Philosophie
Weblog
Weltpolitik
Weltsicht
{{login}}

Fehlerketten

Permalink
Vorheriger: To build a planetNächster: Python vs. PHP
Eingeordnet in: Programmieren

"Fehlerketten" sind der Grund, weshalb ich nicht gerne mit anderer Leute Bibliotheken arbeite. PHP scheint diese Pest, anders als ich, zu mögen, ebenso wie viele PHP-Programmierer. Ein Beispiel: ich wollte eine .docx-Datei öffnen, das XML-Dokument rausziehen, verändern, zurückschreiben und das ganze dann ausgeben. Ich hatte aber zum Testen eine leere .docx erstellt, was zu folgender Fehlerkette führte:

ZipArchive::open("leer.docx"); nichts
ZipArchive::getFromName("word/document.xml"); Warnung, dass das Archiv kaputt sei
DOMDocument::loadXML Warning: DOMDocument::loadXML() [domdocument.loadxml]: Empty string supplied as input in...
DOMDocument::getElementsByTagName("a") nichts
DOMNodeList::item(0)nichts
DOMElement::hasAttribute("href");Fatal error: Call to a member function hasAttribute() on a non-object in...

(der Zugriff auf <a> und href="" ist exemplarisch)

In einer Testumgebung war der Fehler recht schnell zu finden, das hätte aber auch weitaus länger dauern können. Meiner Ansicht (und Praxis) nach sollte bereits ZipArchive::open eine Exception werfen. Man mag zwar einwenden, dass ZipArchive::open auch dazu dient, nicht existierende Archive anzulegen, aber dies ist der Grund, weshalb man an dieser Stelle die Funktionalität in ZipArchive::open und ZipArchive::create teilen sollte.

Spätestens getFromName sollte eine Exception werfen. Die Fehlerbehandlung könnte gründlicher sein (Archiv leer vs. Archiv kaputt).
Vollends gruselig wird aber das Verhalten von DOM. DOMDocument::loadXML akzeptiert NULL als String, danach laufen aber alle Operationen wie üblich weiter, bis man endlich bei einem Fatal Error landet, nämlich, dass man gar kein Objekt hat, um damit zu arbeiten. Allerdings drängt sich hier weitere Idiotie auf: warum erzeugt man nicht ein Standardobjekt mit Standardmethoden, die NULL zurückliefern? Dann wäre die Fehlersuche noch komplizierter.

Das Problem liegt übrigens nicht nur an PHP oder dass es keine starke Typisierung hat. Ähnlichen Bockmist kann man auch in Java verzapfen, da dort jeder Wert mit null substituiert werden kann. Einige der Collections geben stillschweigend null zurück, wenn man auf nicht existierende Werte zugreift - ein ähnlich idotisches Verhalten.

Ich habe einige Grundregeln:

  • Nur Exceptions verwenden. Exceptions erlauben eine abgestufte Fehlerbehandlung und liefern einen detaillierten Backtrace.
  • detaillierte Fehlerbehandlung. Dies ist vor allem wichtig in der Benutzerinteraktion. Wenn der Benutzer z. B. ein falsches Datum eingibt, sag ihm, dass das Format falsch ist, dass es einen 31. Februar nicht gibt oder die Jahreszahl 802 in einem zeitgenössischen Tool keinen Sinn ergibt. Dies gilt auch für Programmierer - ich erinnere mich mit Grausen an MySQL, unter dem sämtliche möglichen Foreign Key-Fehler (Name schon vorhanden, Index fehlt, Typen unverträglich usw. usf.) auf einen Fehler aufliefen.
  • frühe Fehlerbehandlung. Der Fehler sollte so früh wie möglich auftauchen. Im obigen Beispiel wäre das ZipArchive::open(), denn dort lag mein Fehler. Nicht bei DOMElement::hasAttribute. Je weiter Fehler und Fehlermeldung auseinanderliegen, desto schwerer ist die Ursache zu finden.
  • strenge Fehlerbehandlung. Grundsätzlich betrachte ich alles als Fehler, was nicht dem gewünschten Ablauf entspricht. Wenn ich in einem Restaurant ein Gericht bestelle, für das die Zutaten ausgegangen sind, erwarte ich eine Fehlerbehandlung und keinen leeren Teller.

Dazu kommt, dass alle Klassen eine präventive Fehlervermeidung bereitstellen sollten. Im obigen Beispiel müsste ich die DOMNodeList selbst überprüfen, ob sie enthält, was ich verlangt habe. Dies geht an dieser Stelle nur, indem ich die public property DOMNodeList::length auswerte (gruselig). Dies ist dann auch das, was man in vielen Quelltexten sieht:

<?php
$foo 
Bar::getValue("foo");
if(
$foo == NULL) {
    
//Reaktion
}
?>

Ich halte es grundsätzlich so:

<?php
if(Bar::hasValue("foo")) {
    
$foo Bar::getValue("foo");
}
?>

Dies aber auch nur, wenn man kein else { wirf Fehler} hinschreibt. Denn wenn Bar::hasValue("foo") nicht FALSE sein darf, dann kann ich auch gleich Bar::getValue() aufrufen und die Fehlermeldung von Bar::getValue durchschlagen lassen, falls foo doch fehlt.
Dies entspricht meiner Ansicht auch eher OOP, da die einzelnen Module die Funktionalität bereitstellen und man nicht wieder und wieder und wieder die gleichen Prüfungen imperativ aussen herum programmieren muss.

Einige meiner Kollegen halten es für verschwendete Arbeit, Prüfung nach Prüfung zu programmieren. In der Tat kann es langweilen, "Idiotencode" zu schreiben. Regelmässig am Debugger zu hängen macht mir allerdings noch viel weniger Spass.

Kommentieren

Bitte beachten: Kommentare sind nicht sofort sichtbar, sondern werden erst nach einer kurzen Prüfung freigegeben, sofern keine rechtliche Beanstandung vorliegt.
Rechtlich bedenkliche Inhalte werden entweder entschärft oder nicht veröffentlicht.

* Titel  
* Nickname  
* Kommentar