Mailinglisten-Archive |
daniel lorch wrote: > > ist es "guter code" wenn ich die fehlermeldung per pointer übergebe? ich > mache ein beispiel: > > function hallo(&$err) > { > if(!mach_was()) > { > $err="Es ist fehlgeschlagen, weil ..."; > return false; > } > > return true; > } > > if(hallo($err)) > echo "Alles Oki"; > else > echo "FEHLER: $err"; Fehlerbehandlung in PHP ist ein heißes Thema: es geht einfach nicht richtig sauber, die Sprache bietet nicht die notwendigen Mittel. Die Behandlung von Fehlern in PHP kann den Ansätzen aus verschiedenen Sprachen folgen. C und Java stellen zwei wesentliche Varianten vor, die jeweils ihre spezifischen Vor- und Nachteile haben. PHP scheint einen Zwischenweg zu nehmen. Klassiche Fehlerbehandlungarbeitet mit zwei global ansprechbaren Variablen $errno und $error. $errno enthält einen eindeutigen Integerwert zur Identifizierung der Stelle im Programm, die den Fehler meldet. $error enthält eine Klartext Fehlermeldung. $errno und $error werden i.d.R. durch eine Funktion gesetzt, die von fehlerverursachenden Code aufgerufen wird. Handelt es sich bei dem Code der den Fehler verursacht um eine Funktion, wird die Abarbeitung unterbrochen. Ob ein Wert und wenn ja welcher zurückgeliefert wird ist unklar. Ebenso die Frage ob der Fehler direkt ausgegeben wird. Übertragt man dies in die Welt von PHP ergibt sich solcher oder ähnlicher Code (PHP3, prozedual): <?php $errno = false; $error = ""; function set_error($no, $msg) { global $errno, $error; $errno = $no; $error = $msg; } function cause_error() { set_error(1, "Just a silly test"); return; } cause_error(); if ($errno) die($error); ?> Dieser Code ist sehr einfach, bietet sehr viel Flexibilität und genügt vollauf zur Fehlerbehandlung in kleinen Skripten. Doch das Erkennen von Fehlern ist etwas mühsam. Es gibt globale Variablen, die nicht als "protected" ("schreibgeschützt") deklariert werden können und nur allzu leicht irgendwo einmal überschrieben werden können. Schöner wäre ein derartiger Ansatz (PHP3, prozedual): <?php [...] function cause_error() { set_error(1, "Just a silly test"); return false; } if (!cause_error()) handle_error(); ?> Nun ist dies zwar kürzer und auch etwas "sicherer" als die Abfrage von $errno aber es wirft ein neues Problem auf. Die kann man einen gültigen Returnvalue von einem ungültigen unterscheiden? false könnte eine korrekte Antwort von ist_Primzahl() sein. Dies gilt selbstverständlich auch für jeden anderen Wert der vereinbart wurde um einen Fehler zu kennzeichen. Selbst wenn false in keiner Funktion ein gültiger Returnwert ist, sind Schwierigkeiten vorprogrammiert. Die Funktion verspricht ein Array zu liefern, welchselt bei einem Fehler jedoch den Returntype. Wird der Fehler nicht abgefangen, drohen Folgefehler. Statt des erwarteten Array wurde ein skalarer Wert geliefert, Arrayzugriffe schlagen fehl. Dieses Problem wurde schnell erkannt und ein neues Konstrukt try-catch eingeführt. Potentiell fehlerverursachender Code kann in Java (C++) in einem try Block gehalten werden und im anschließenden catch wird eine etwaige Ausnahme (exception) "gefangen". <?java_schematisch try { das macht Ärger; } catch ( Ausnahme Testfehler ) { Fehler behandeln; } ?> PHP kennt dieses Konstrukt nicht und wird es wohl auch nicht in naher Zukunft kennenlernen. Ganz im Gegenteil die Entwicklungen laufen gerade in zwei verschiedene Richtungen. Eine Exception in Java ist ein von Throwable abgeleitetes Objekt, welches die Fehlermeldung und den Typ (Typ des Objekt) kapselt und einige Funktionen zur Abfrage des Fehlers bereithält. Die Vorteile der Kapselung liegen auf der Hand, nur leider paßt dieses Konzept kaum in die PHP Welt. Dennoch favorisiere ich diesen Weg. In PHP4 gibt es drei neue, bislang undokumentiere Funktionen, die den klassischen Weg ausbauen: - trigger_error( string $message [, int $error_type ] ) - set_error_handler( string $name_of_error_handler_function ) - restore_error_handler() (plus einige Aliase) trigger_error() erzeugt eine Fehlermeldung von einem bestimmten Typ ( E_NOTICE, E_WARNING, E_ERROR ), die der Funktion abgefangen werden kann, die mit set_error_handler() spezifiziert wurde. restore_error_handler() ist das "Undo" für set_error_handler(). Diese Lösung hat einen gewissen Charme, weil eine zentrale Funktion zur Fehlerbehandlung zur Verfügung steht, die den Fehler drucken, loggen, vermailen oder sonstwie bearbeiten kann. Innerhalb eines Skripts durchaus ein guter Weg. Was passiert jedoch, wenn Code wiederverwendet werden soll, Bibliotheken eingebunden werden und jede Bibliothek ihre eigenen Error Handler definiert? Derzeit schlage ich deshalb einen Zwischenweg vor der sich ein klein wenig aus allen Töpfen bedient. Etwas Kapselung von Java gepaart mit einem optionalen trigger_error(), halt_on_error und auto_handle_error (PHP4, Mischung aus PEAR und Ulk). <?php class error { var $message = ""; var $file = ""; var $line = -1; var $type = E_ERROR; var $trigger_error = false; var $halt_on_error = false; var $auto_handle_error = false; function error($message, $file = _FILE_, $line = _LINE_) { $this->message = $message; $this->file = $file; $this->line = $line; if ($trigger_error) trigger_error($this->getLocalizedMessage(), $this->type); if ($auto_handle_error) $this->handle_error(); if ($halt_on_error) die($this->getLocalizedMessage()); } function getMessage() { [...] } function getLocalizedMessage() { [...] } function handleError() { ... } } ?> Die Klassenvariablen trigger_error, halt_on_error und auto_handle_error sind per Default auf false gesetzt und sollten nur während der Entwicklungsphase andere Werte annehmen. Von dieser Basisklasse werden die eigenen Fehlerklassen abgeleitet. Will man einen Fehler "werfen" so greift man wieder auf einen Zwischenweg (PHP4, OO): <?php class joedoes { var $err; function causeError() { $this->err = new ErrorItsMine("Just a silly test.", __FILE__, __LINE__); } function isError() { return ( if ( is_object($this->err) && (isset($this->err->type)) && ("Error"==substr($this->err->type)) ) ) ? true : false; } } ?> (Geht auch mit :: bei kleinen Änderungen) Leider ist das "catchen" jedoch wieder "aufwendig": <?php $j = new joedoes; $ok = $j->causeError(); if (isError($ok)) print $ok->getLocalizedMessage(); ?> Wie man sieht ist auch das nicht die ideale Lösung, dennoch wird es wahrscheinlich der Weg sein, den PEAR nimmt. Etwas hier, etwas da und alle betteln Zeev an try-catch() zu implementieren. Was sollst Du nun machen? Die Referenzen bringen Dir überhaupt nichts, im Gegenteil ich habe die Erfahrung gemacht das sie etwas langsamer sind als ein passed-by-value (10%). Wenn Du auf PHP3 setzt nimm den ersten Ansatz den ich skizziert habe, bei PHP4 kannst Du auf trigger_error() setzen und bei großen Projekten solltest Du mit dem PEAR Ansatz experimentieren. Ulf -- Ulf Wendel NetUSE AG, Siemenswall, 24107 Kiel, Germany Fon: +49 431 386435 00 -- Fax: +49 431 386435 99
php::bar PHP Wiki - Listenarchive