Auf Warnings, Notices und sogar Fatal Errors reagieren
In besonders kritischen Scripten baue ich gern zur Sicherheit noch eine erweiterte Log-Funktion ein, die auch noch aufgerufen wird bei einem FATAL ERROR oder bei einem exit() oder die() in einer Fremdbibliothek (pfui). Man kann so auch beim Vorkommen einer Notice das Script beenden, das kann manchmal sehr sinnvoll sein. Natürlich kann man mit Hilfe der error_log Einstellung in der php.ini und einem error_reporting “E_ALL^E_STRICT” diese Fehler auch wegloggen, aber man ist nicht flexibel wenn man im Code darauf reagieren möchte. Beispielsweise könnte es sein dass bei einem FATAL ERROR noch etwas aufzuräumen ist, damit kein Müll hinterlassen wird.
Nehmen wir folgendes Beispiel:
<?php register_shutdown_function('shutdownFunction'); function shutDownFunction() { $error = error_get_last(); if ($error['type'] == 1) { // log error to syslog, send email or sms, this should not happen! } } echo nonExistingFunction();
Bei sauberem Code sollte das natürlich nicht passieren. Aber es gibt Situationen in denen ein Fatal Error passieren kann, wenn man beispielsweise eine Funktion aufruft mittels call_user_func() ohne eine vorherige is_callable() Prüfung, oder oder.
OK, nächstes Beispiel:
<?php $fileContent = file_get_contents('/data/source.txt'); $fileContent = str_replace('Michael', 'Marcel', $fileContent); file_put_contents('/data/target.txt', $fileContent);
Wir legen dieses kleine Script auf einen Server und es läuft wunderbar. Doch plötzlich ist die source.txt nicht da. Was passiert? Es gibt zwar eine Warning, aber das Script bricht nicht ab, $fileContent wird mit einem Leerstring gefüllt, und die Datei target.txt wird geschrieben, sie ist nun defekt (leer), und ein anderes Programm wird mit dieser defekten Datei weiterarbeiten.
Wir hätte man dieses Problem verhindern können? Die naheliegende Lösung wäre natürlich ein file_exists() gewesen, aber genau das haben wir ja vergessen. Wir hätten die Warnung von PHP ernst nehmen können und beim Warning des file_get_contents() Befehls abbrechen können, wir könnten generell bei jeder Notice oder Warning, die irgendwo passiert im Code, den Fehler loggen und das Script abbrechen. Ein Abbruch ist besser als ein Script, das in einem undefinierten Zustand weiterläuft.
Fügen wir ein paar Zeilen hinzu:
<?php // error handler function function myErrorHandler($errno, $errstr, $errfile, $errline) { if (!(error_reporting() & $errno)) { // This error code is not included in error_reporting return false; } $errorMsg = 'In file '.$errfile.' on line '.$errline.': '.$errno.' - '.$errstr; mail('admin@domain.de', 'Error occured', $errorMsg); exit; } set_error_handler("myErrorHandler"); $fileContent = file_get_contents('/data/source.txt'); $fileContent = str_replace('michael', 'marcel', $fileContent); file_put_contents('/data/target.txt', $fileContent);
Wir definieren also einen eigenen Error-Handler, der bei allen Arten von Errors (Notices, Warnings etc) aufgerufen wird. Solange der ErrorLevel von error_reporting abgedeckt ist, schicken wir uns den Fehler per E-Mail und beenden das Script sofort. Eine nicht geschriebene target.txt ist in diesem Fall besser als eine defekte.
Eventuell auch interessant ist die Umwandlung von Notices, Warnings etc. in Exceptions, man nimmt einfach folgenden Error-Handler:
function myErrorHandler($errno, $errstr, $errfile, $errline ) { throw new Exception($errstr, 0, $errno, $errfile, $errline); }
Dann kann man Notices/Warnings etc. catchen