PHP/Laufzeitfehler

Aus Mikiwiki
< PHP
Version vom 19. Januar 2009, 18:14 Uhr von Michi (Diskussion | Beiträge) (Die Seite wurde neu angelegt: == Fehlerklassen == PHP unterscheidet eine Anzahl von Fehlerklassen unterschiedlicher Schwere, durch Konstanten dargestellt. {| class=wikitable width=100% ! Bitwert !...)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

Fehlerklassen

PHP unterscheidet eine Anzahl von Fehlerklassen unterschiedlicher Schwere, durch Konstanten dargestellt.

Bitwert Fehlername Beschreibung
1 E_ERROR Normale Fehler in Funktionen bzw. Fehler im Programmablauf.
2 E_WARNING Normale Warnungen.
4 E_PARSE Parserfehler bzw. Fehler in der Syntax.
8 E_NOTICE Nachrichten.
16 E_CORE_ERROR Fehler des PHP-Kernels.
32 E_CORE_WARNING Warnung des PHP-Kernels.
64 E_COMPILE_ERROR Zend-Compiler-Fehler.
128 E_COMPILE_WARNING Zend-Compiler-Warnung.
256 E_USER_ERROR Diese Konstanten steuern das Verhalten der benutzerdefinierten Fehlerbehandlung mit der Funktion "user_error".
512 E_USER_WARNING
1024 E_USER_NOTICE
2047 E_ALL Alle Fehler.
2048 E_STRICT Nachrichten des Laufzeitsystems, die Vorschläge zur Verbesserung des Codes bzw. zur PHP5-Kompatibilität enthalten.

Die Fehler werden als Bitfeld zusammengefasst und ergeben einen Fehlerstatus. Ausgaben erfolgen nur dann im Webbrowser, wenn das entsprechende Fehlerniveau erreicht wurde. Der Standardwert ist "E_ALL", eine Kombination aller Bitwerte ausser "E_STRICT". Die Einstellung in der Datei "php.ini" kann mit dem folgenden Eintrag geändert werden, etwa um Nachrichten zu unterdrücken. Dies erlaubt die Anzeige aller Fehler ausser von Nachrichten. Vor allem ältere und unsauber programmierte Skripte erzeugen sonst ständig Fehlerausgaben, obwohl sie ablauffähig sind. Der Operator "~" negiert ein Bit (es handelt sich bei den Konstanten um Bitwerte).

error_reporting = E_ALL & ~E_NOTICE

Wenn die Datei "php.ini" nicht geändert werden kann, so kann auch die Funktion "ini_set" benutzt werden.

ini_set("error_reporting", E_ALL & ~E_NOTICE & ~E_STRICT);

Darüberhinaus können auch interne Fehler (sogenannte Kernelfehler) erkannt und mit der Funktion "error_reporting" ausgegeben werden. Als Kernel wird in diesem Zusammenhang der Teil des PHP-Interpreters bezeichnet, der die Kernfunktionen und Sprachkonstrukte realisiert.

Die folgende Anweisung schaltet alle Laufzeitfehler ab; Syntaxfehler lassen sich generell nicht unterdrücken.

error_reporting(0);

Es ist sinnvoll, auf vom Interpreter erzeugte Fehler während der Laufzeit gezielt zu reagieren. Dies erspart oft viele zusätzliche Prüfungen und Abfragen, die ihrerseits wieder Fehlerquellen darstellen.

Fehler abfangen

Oft werden weniger schwere Fehler ausgegeben, die auf unsichere Funktionsauswertungen oder fehlende Parameter zurückzuführen sind. Grundsätzlich sollten Skripte natürlich fehlerfrei arbeiten, manchmal treten Fehler aber nur selten auf oder werden bewusst in Kauf genommen. Solche Fehler können unterdrückt werden, indem vor den Aufruf einer beliebigen Funktion das Zeichen "@" gestellt wird. Normalerweise gibt die Funktion bei fehlerhaftem Aufruf "FALSE" zurück. Die Auswertung kann deshalb gut mit "if"-Anweisungen erfolgen.

<?php
if (!@fopen("xxx", "r")) 
{
  echo "Datei 'xxx' nicht gefunden.";
} 
else 
{
  echo "Ausführung ok.";
}
?>
Datei 'xxx' nicht gefunden.

Bei der Unterdrückung von Funktionsaufrufen nicht vorhandener Funktionen wird der gesamte "if"-Block nicht ausgeführt. Es ist sinnvoll, Skripte erst auszutesten und dann an kritischen Stellen Systemfehler zu unterdrücken. Im Sinne einer professionellen Programmierung ist es allgemein nicht empfehlenswert, die Fehlerbehandlung dem System zu überlassen. Das sollte selber erledigt werden: Im letzten Beispiel wäre der Dateitest mit "file_exists" sicher sinnvoller.

Fehleranzeige anpassen

In der Konfigurationsdatei "php.ini" kann das Format der Fehlerausgabe eingestellt werden. Erlaubt ist jede Art von HTML-Tag. Die Ausgabe erfolgt ohne weitere Bearbeitung. Die folgende Form schreibt alle Fehlermeldungen in Rot.

error_prepend_string = "<font color=#ff0000>"
error_append_string  = "</font>"

Zur Änderung des Werts zur Laufzeit kann wiederum "ini_set" verwendet werden.

Fehlersuche zur Entwurfszeit

Während der Erstellung einer Anwendung sind oft umfassendere Fehlerhinweise und Warnungen notwendig. Zur Laufzeit, wenn Benutzer mit dem System arbeiten, sind sie dagegen unerwünscht. Zwei Optionen in der Datei "php.ini" schalten diese erweiterten Fehleranzeigen ein und aus.

## Allgemeines Einschalten der Laufzeitfehleranzeige
display_errors = "1";
## Fügt auch Fehler hinzu, die beim Starten des PHP-Moduls auftreten können (z. B. fehlende Module)
display_startup_errors = "0";

Benutzerdefinierte Fehlerbehandlung

PHP 5 verfügt über einige Funktionen, mit denen sich ein eigenes Fehlermanagement aufbauen lässt. PHP verfügt über einen sogenannten "Errorhandler", der sich mit der Funktion "set_error_handler" austauschen lässt. Als Argument ist der Name einer eigenen Funktion anzugeben.

set_error_handler("ls_errorhandler");

Ab diesem Punkt ist man selber für die Ausgabe aller Fehlermeldungen zuständig. Eine benutzereigene Variante der Fehlerverwaltung könnte wie folgt aussehen.

<?php
## Name     error_userhandler.php

class Errors
{
  function Error_Handler($errno, $errstr, $errfile, $errline) 
  {
    switch ($errno) 
    {  
      case E_USER_ERROR:
        echo "<p><span style=\"color:red\"><b>FEHLER [$errno]</b> $errstr</span><br />\n";
        echo "Ausgelöst auf Zeile <b>$errline</b> in Skript <b> $errfile</b><br />\n";
        echo "PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
        echo "Programmabbruch...</p>\n";
        exit -1;
        break;
      case E_USER_WARNING:
        echo "<span style=\"color:red\"><b>WARNUNG [$errno]</b> $errstr<br />\n";
        echo "Skript <b>$errfile</b>, Zeile <b>$errline</b></span>\n";
        break;
      case E_USER_NOTICE:
        echo "<p><span style=\"color:red\"><b>HINWEIS [$errno]</b> $errstr<br />\n";
        echo "Skript <b>$errfile</b>, Zeile <b>$errline</b></span></p>\n";
        break;
      case E_NOTICE:
        echo "<p><span style=\"color:red\"><b>PHP-Nachricht [$errno]</b> $errstr<br />\n";
        echo "Skript <b>$errfile</b>, Zeile <b>$errline</b></span></p>\n";
        break;
      case E_PARSE:
        echo "<p><span style=\"color:red\"><b>PHP-Parserfehler [$errno]</b> $errstr<br />\n";
        echo "Skript <b>$errfile</b>, Zeile <b>$errline</b></span></p>\n";
        break;
      case E_WARNING:
        echo "<p><span style=\"color:red\"><b>PHP-Warnung [$errno]</b> $errstr<br />\n";
        echo "Skript <b>$errfile</b>, Zeile <b>$errline</b></span></p>\n";
        break;
      case E_ERROR:
        echo "<p><span style=\"color:red\"><b>PHP-FEHLER [$errno]</b> $errstr<br />\n";
        echo "Skript <b>$errfile</b>, Zeile <b>$errline</b></span></p>\n";
        break;
    }
  }
}
## Setzen der Rückruffunktion, wobei Klasse und Methode in einem Schritt zugewiesen werden
error_reporting(E_ALL);
set_error_handler(array("Errors", "Error_Handler"));

## Provolation von Fehlern zu Testzwecken
echo "<p>Start der eigenen Fehlerfunktion:</p>\n";
echo $cc;
fopen("llk", "r");
$cc = 0;
if ($cc == 0) 
{
  ## Auslösung des eigenen Fehlers
  trigger_error("0 ist für \$cc nicht erlaubt", E_USER_NOTICE);
}
?>

Start der eigenen Fehlerfunktion:

PHP-Nachricht [8] Undefined variable: cc
Skript /WWW/test/test.php, Zeile 48

PHP-Warnung [2] fopen(llk) [function.fopen]: failed to open stream: No such file or directory
Skript /WWW/test/test.php, Zeile 49

HINWEIS [1024] 0 ist für $cc nicht erlaubt
Skript /WWW/test/test.php, Zeile 53

Wenn eine Rückruffunktion angegeben werden kann, kann statt einer normalen Funktion immer auch ein Array mit zwei Zeichenketten angegeben werden. Die erste bestimmt den Namen einer Klasse und die zweite den Namen der Methode in dieser Klasse, die als Rückruffunktion benutzt werden soll.

Mit der eigenen Fehlerbehandlung ist es nicht möglich, Parserfehler zu unterdrücken, da Parserfehler die Funktionsfähigkeit des Systems insgesamt und damit auch die eigenen Fehlerbehandlungsfunktionen in Frage stellen.

Fehlerbehandlung mit Testfunktionen

Fehler treten oft erst unter bestimmten Umgebungsbedingungen auf. PHP5 kennt Funktionen, mit denen ein Test ausgeführt werden kann, ohne dass ein Laufzeitfehler auftritt. Um robuste Skripte zu erhalten, sollten derartige Bedingungen schon bei der Entwicklung simuliert werden. Die Schwierigkeit besteht meist darin, den Fehler koordiniert abzufangen und die Testbedingungen ein- und auszuschalten.

Mit der Funktion "assert" kann eine Bedingung getestet werden. Sie gibt "FALSE" zurück, wenn ein Laufzeitfehler aufgetreten wäre. Die Ausgabe gibt folgende drei Fehler aus:

  1. Eine Warnung, dass die Division durch 0 misslang.
  2. Eine weitere Warnung, dass die Funktion "assert" fehlschlug.
  3. Die eigene, durch "assert" provozierte Fehlerbehandlung mit der Funktion "trigger_error".
<?php
## Einbindung der Klasse Errors aus dem vorigen Beispiel

error_reporting(E_ALL);
set_error_handler(array('Errors', 'Error_Handler'));

echo "Start der eigenen Fehlerfunktion:<br />";
$cc = 100;
$dd = 0;
## Option ASSERT_ACTIVE aktiviert oder deaktiviert Funktion "assert"
assert_options(ASSERT_ACTIVE, 1);
## Warnungen werden unterdrückt
assert_options(ASSERT_QUIET_EVAL, 1);
if (!assert($cc / $dd)) {
  ## Provokation einer Division durch 0
  trigger_error('Division durch 0', E_USER_WARNING);
}

?>
Start der eigenen Fehlerfunktion:

PHP-Warnung [2] Division by zero
Skript /WWW/test/test.php, Zeile 52

PHP-Warnung [2] assert() [function.assert]: Assertion failed
Skript /WWW/test/test.php, Zeile 52

WARNUNG [512] Division durch 0
Skript /WWW/test/test.php, Zeile 53

So lässt sich gut steuern, ob die Warnungen unterdrückt werden und kritische Fehler über "assert" eigene Fehler auslösen.

Werden die Warnungen generell unterdrückt und wird nicht mit "assert" gearbeitet, so sind kritische Fehlerstellen nicht mehr explizit zu erkennen. Wird im Skript jeder theoretisch mögliche Zustand verarbeitet (was nicht immer wirklich möglich ist), so ist "assert" natürlich überflüssig, denn einen darüberhinaus gehenden "Ausnahmezustand" kann es ja nicht geben.