mirror of
https://github.com/php/doc-de.git
synced 2026-03-23 23:02:13 +01:00
623 lines
18 KiB
XML
623 lines
18 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- EN-Revision: c81a48e58fc530a74827316027fae74668d17a1d Maintainer: sammywg Status: ready -->
|
|
<!-- Reviewed: yes -->
|
|
<!-- Rev-Revision: 2e8ef0a1bd98243cb2c6c5c627a195bb53a7a440 Reviewer: samesch -->
|
|
<chapter xml:id="language.exceptions" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Exceptions (Ausnahmen)</title>
|
|
<para>
|
|
PHP hat ein Exceptionmodell ähnlich dem anderer Programmiersprachen. Eine
|
|
Exception kann in PHP geworfen (&throw;) und abgefangen (&catch;) werden.
|
|
Um das Abfangen potentieller Exceptions zu ermöglichen, sollte der jeweilige
|
|
Code von einem &try;-Block umschlossen werden. Jeder &try;-Block muss
|
|
mindestens einen zugehörigen &catch;- oder &finally;-Block besitzen.
|
|
</para>
|
|
<para>
|
|
Wenn eine Exception geworfen wird und der aktuelle Funktionsbereich keinen
|
|
&catch;-Block hat, steigt die Exception im Aufrufstapel bis zur aufrufenden
|
|
Funktion auf, bis sie einen passenden &catch;-Block findet. Alle
|
|
&finally;-Blöcke, auf die sie unterwegs trifft, werden ausgeführt. Wenn der
|
|
Aufrufstapel bis in den globalen Bereich abgewickelt ist, ohne auf einen
|
|
passenden &catch;-Block zu stoßen, bricht das Programm mit einem fatalen
|
|
Fehler ab, es sei denn, es wurde ein globaler Exception-Handler gesetzt.
|
|
</para>
|
|
<para>
|
|
Das geworfene Objekt muss eine Instanz von (&instanceof;)
|
|
<interfacename>Throwable</interfacename> sein. Der Versuch ein Objekt
|
|
werfen, das das nicht ist, wird einen fatalen PHP-Fehler zur Folge
|
|
haben.
|
|
</para>
|
|
<para>
|
|
Seit PHP 8.0.0 ist das Schlüsselwort &throw; ein Ausdruck und kann in jedem
|
|
Ausdruckskontext verwendet werden. In früheren Versionen war es eine
|
|
Anweisung und musste in einer eigenen Zeile stehen.
|
|
</para>
|
|
|
|
<sect1 annotations="chunk:false" xml:id="language.exceptions.catch">
|
|
<title><literal>catch</literal></title>
|
|
<para>
|
|
Ein &catch;-Block definiert, wie auf eine geworfene Exception reagiert
|
|
werden soll. Ein &catch;-Block definiert eine oder mehrere Arten von
|
|
Exceptions oder Fehlern, die er behandeln kann, und optional eine Variable,
|
|
der die Exception zugewiesen werden soll (vor PHP 8.0.0 war die Variable
|
|
erforderlich). Der erste &catch;-Block, auf den eine geworfene Exception
|
|
oder ein Fehler trifft, der mit dem Typ des geworfenen Objekts
|
|
übereinstimmt, behandelt das Objekt.
|
|
</para>
|
|
<para>
|
|
Mehrere &catch;-Blöcke können verwendet werden, um verschiedene Klassen von
|
|
Exceptions abzufangen. Wenn innerhalb des &try;-Blocks keine Exception
|
|
geworfen wird, wird die normale Programmausführung nach dem letzten in
|
|
Folge definierten &catch;-Block fortgesetzt. Exceptions können innerhalb
|
|
eines &catch;-Blocks geworfen (oder erneut geworfen) werden. Falls nicht,
|
|
wird die Ausführung nach dem &catch;-Block, der geworfen wurde,
|
|
fortgesetzt.
|
|
</para>
|
|
<para>
|
|
Wenn eine Exception geworfen wird, führt PHP den Programmcode hinter der
|
|
werfenden Anweisung nicht aus, sondern versucht, den ersten passenden
|
|
&catch;-Block zu finden. Falls eine Exception nicht abgefangen wird, wird
|
|
ein fataler Fehler mit einer
|
|
"<literal>Uncaught Exception ...</literal>"-Nachricht ausgegeben, sofern
|
|
keine Behandlung mittels <function>set_exception_handler</function>
|
|
definiert wurde.
|
|
</para>
|
|
<para>
|
|
Seit PHP 7.1.0 kann ein &catch;-Block mehrere Exceptions getrennt durch
|
|
Pipe-Zeichen (<literal>|</literal>) angeben. Dies ist nützlich, wenn
|
|
unterschiedliche Exceptions von unterschiedlichen Klassenhierarchien gleich
|
|
behandelt werden sollen.
|
|
</para>
|
|
<para>
|
|
Seit PHP 8.0.0 ist der Variablenname für eine abgefangene Exception
|
|
optional. Wird er nicht angegeben, wird der &catch;-Block trotzdem
|
|
ausgeführt, hat aber keinen Zugriff auf das geworfene Objekt.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 annotations="chunk:false" xml:id="language.exceptions.finally">
|
|
<title><literal>finally</literal></title>
|
|
<para>
|
|
Ein &finally;-Block kann auch nach den &catch;-Blöcken oder stattdessen
|
|
definiert werden. Egal, ob eine Exception geworfen wurde, wird der Code
|
|
innerhalb des &finally;-Blocks immer nach den &try;- und &catch;-Blöcken
|
|
ausgeführt, bevor die normale Ausführung fortgesetzt wird.
|
|
</para>
|
|
<para>
|
|
Eine bemerkenswerte Wechselwirkung besteht zwischen dem &finally;-Block
|
|
und einer &return;-Anweisung. Wird eine &return;-Anweisung innerhalb der
|
|
&try;- oder &catch;-Blöcke angetroffen, wird der &finally;-Block dennoch
|
|
ausgeführt. Außerdem wird die &return;-Anweisung ausgewertet, wenn sie
|
|
angetroffen wird, aber das Ergebnis wird erst nach dem &finally;-Block
|
|
zurückgegeben. Des Weiteren wird, wenn der &finally;-Block ebenfalls eine
|
|
&return;-Anweisung enthält, der Wert aus dem &finally;-Block zurückgegeben.
|
|
</para>
|
|
<para>
|
|
Eine weitere bemerkenswerte Wechselwirkung tritt auf, wenn sowohl im &try;-
|
|
als auch im &finally;-Block eine Exception geworfen wird. In diesem Fall
|
|
wird die vom &finally;-Block geworfene Exception weitergegeben, während die
|
|
vom &try;-Block geworfene Exception als deren vorherige Exception verwendet
|
|
wird.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 annotations="chunk:false" xml:id="language.exceptions.exception-handler">
|
|
<title>Der globale Exception-Handler</title>
|
|
<para>
|
|
Wenn eine Exception in den globalen Bereich aufsteigen darf, kann sie durch
|
|
einen globalen Exception-Handler abgefangen werden, falls gesetzt. Die
|
|
Funktion <function>set_exception_handler</function> kann eine Funktion
|
|
festlegen, die anstelle eines &catch;-Blocks aufgerufen wird, wenn kein
|
|
anderer Block aufgerufen wird. Der Effekt ist im Wesentlichen derselbe, als
|
|
ob das gesamte Programm in einen &try;-&catch;-Block mit dieser Funktion
|
|
als &catch; verpackt wäre.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 annotations="chunk:false" xml:id="language.exceptions.notes">
|
|
&reftitle.notes;
|
|
|
|
<note>
|
|
<para>
|
|
Interne PHP-Funktionen verwenden in den meisten Fällen
|
|
<link linkend="ini.error-reporting">Error-Reporting</link>, nur moderne
|
|
<link linkend="language.oop5">objektorientierte</link> Erweiterungen
|
|
nutzen Exceptions. Fehler können allerdings einfach mittels
|
|
<link linkend="class.errorexception">ErrorException</link> in eine
|
|
Exception umgewandelt werden. Diese Technik funktioniert jedoch nur bei
|
|
nicht-fatalen Fehlern.
|
|
</para>
|
|
<example>
|
|
<title>Fehlermeldungen in Exceptions umwandeln</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
function exceptions_error_handler($severity, $message, $filename, $lineno) {
|
|
throw new ErrorException($message, 0, $severity, $filename, $lineno);
|
|
}
|
|
|
|
set_error_handler('exceptions_error_handler');
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</note>
|
|
<tip>
|
|
<para>
|
|
Die <link linkend="intro.spl">Standard PHP Library (SPL)</link> bietet
|
|
eine große Anzahl
|
|
<link linkend="spl.exceptions">eingebauter Exceptions</link>.
|
|
</para>
|
|
</tip>
|
|
</sect1>
|
|
|
|
<sect1 annotations="chunk:false" xml:id="language.exceptions.examples">
|
|
&reftitle.examples;
|
|
|
|
<example>
|
|
<title>Eine Exception werfen</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
function inverse($x) {
|
|
if (!$x) {
|
|
throw new Exception('Division durch Null.');
|
|
}
|
|
return 1/$x;
|
|
}
|
|
|
|
try {
|
|
echo inverse(5) . "\n";
|
|
echo inverse(0) . "\n";
|
|
} catch (Exception $e) {
|
|
echo 'Exception abgefangen: ', $e->getMessage(), "\n";
|
|
}
|
|
|
|
// Ausführung fortsetzen
|
|
echo "Hallo Welt\n";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
0.2
|
|
Exception abgefangen: Division durch Null
|
|
Hallo Welt
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Exceptionbehandlung mit einem &finally;-Block</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
function inverse($x) {
|
|
if (!$x) {
|
|
throw new Exception('Division durch Null.');
|
|
}
|
|
return 1/$x;
|
|
}
|
|
|
|
try {
|
|
echo inverse(5) . "\n";
|
|
} catch (Exception $e) {
|
|
echo 'Exception abgefangen: ', $e->getMessage(), "\n";
|
|
} finally {
|
|
echo "Erstes finally.\n";
|
|
}
|
|
|
|
try {
|
|
echo inverse(0) . "\n";
|
|
} catch (Exception $e) {
|
|
echo 'Exception abgefangen: ', $e->getMessage(), "\n";
|
|
} finally {
|
|
echo "Zweites finally.\n";
|
|
}
|
|
|
|
// Ausführung fortsetzen
|
|
echo "Hallo Welt\n";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
0.2
|
|
Erstes finally.
|
|
Exception abgefangen: Division durch Null.
|
|
Zweites finally.
|
|
Hallo Welt
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Wechselwirkung zwischen dem &finally;-Block und &return;</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
function test() {
|
|
try {
|
|
throw new Exception('foo');
|
|
} catch (Exception $e) {
|
|
return 'catch';
|
|
} finally {
|
|
return 'finally';
|
|
}
|
|
}
|
|
|
|
echo test();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
finally
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Verschachtelte Exceptions</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
class MyException extends Exception { }
|
|
|
|
class Test {
|
|
public function testing() {
|
|
try {
|
|
try {
|
|
throw new MyException('foo!');
|
|
} catch (MyException $e) {
|
|
// Exception erneut werfen
|
|
throw $e;
|
|
}
|
|
} catch (Exception $e) {
|
|
var_dump($e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
$foo = new Test;
|
|
$foo->testing();
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
string(4) "foo!"
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Behandlung mehrerer Exceptions in einem Catch-Block</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
class MyException extends Exception { }
|
|
|
|
class MyOtherException extends Exception { }
|
|
|
|
class Test {
|
|
public function testing() {
|
|
try {
|
|
throw new MyException();
|
|
} catch (MyException | MyOtherException $e) {
|
|
var_dump(get_class($e));
|
|
}
|
|
}
|
|
}
|
|
|
|
$foo = new Test;
|
|
$foo->testing();
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
string(11) "MyException"
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Catch-Block ohne Angabe einer Variablen</title>
|
|
<para>Erst ab PHP 8.0.0 erlaubt.</para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
class SpecificException extends Exception {}
|
|
|
|
function test() {
|
|
throw new SpecificException('Oopsie');
|
|
}
|
|
|
|
try {
|
|
test();
|
|
} catch (SpecificException) {
|
|
print "Eine SpecificException wurde geworfen, aber die Details interessieren uns nicht.";
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Eine SpecificException wurde geworfen, aber die Details interessieren uns nicht.
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Als Ausdruck werfen</title>
|
|
<para>Erst ab PHP 8.0.0 erlaubt.</para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
function test() {
|
|
do_something_risky() or throw new Exception('Es hat nicht funktioniert');
|
|
}
|
|
|
|
function do_something_risky() {
|
|
return false; // Fehler simulieren
|
|
}
|
|
|
|
try {
|
|
test();
|
|
} catch (Exception $e) {
|
|
print $e->getMessage();
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Es hat nicht funktioniert
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Exception in try und in finally</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
try {
|
|
try {
|
|
throw new Exception(message: 'Dritte', previous: new Exception('Vierte'));
|
|
} finally {
|
|
throw new Exception(message: 'Erste', previous: new Exception('Zweite'));
|
|
}
|
|
} catch (Exception $e) {
|
|
var_dump(
|
|
$e->getMessage(),
|
|
$e->getPrevious()->getMessage(),
|
|
$e->getPrevious()->getPrevious()->getMessage(),
|
|
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
|
|
);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
string(5) "Erste"
|
|
string(6) "Zweite"
|
|
string(5) "Dritte"
|
|
string(6) "Vierte"
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.exceptions.extending">
|
|
<title>Exceptions erweitern</title>
|
|
<para>
|
|
Eine benutzerdefinierte Exceptionklasse kann durch Ableitung von der
|
|
eingebauten Exceptionklasse erstellt werden. Die unten angegebenen Methoden
|
|
und Eigenschaften zeigen, was innerhalb der Kindklasse von der eingebauten
|
|
Exceptionklasse verfügbar ist.
|
|
</para>
|
|
<example>
|
|
<title>Die eingebaute Exceptionklasse</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Exception implements Throwable
|
|
{
|
|
protected $message = 'Unknown exception'; // Exceptionmitteilung
|
|
private $string; // __toString-Cache
|
|
protected $code = 0; // Benutzerdefinierte Fehlernummer
|
|
protected $file; // Quelldateiname der Exception
|
|
protected $line; // Quelldateizeile der Exception
|
|
private $trace; // Rückverfolgung
|
|
private $previous; // Vorherige Exception, falls verschachtelte Exception
|
|
|
|
public function __construct($message = '', $code = 0, ?Throwable $previous = null);
|
|
|
|
final private function __clone(); // Verhindert klonen von Exceptions
|
|
|
|
final public function getMessage(); // Mitteilung der Exception
|
|
final public function getCode(); // Fehlercode der Exception
|
|
final public function getFile(); // Quelldateiname
|
|
final public function getLine(); // Quelldateizeile
|
|
final public function getTrace(); // Array zum Rückverfolgen
|
|
final public function getPrevious(); // Vorherige Exception
|
|
final public function getTraceAsString(); // Formatierter String der Rückverfolgung
|
|
|
|
// Überschreibbar
|
|
public function __toString(); // Formatierter String für die Ausgabe
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<para>
|
|
Wenn eine Klasse die eingebaute Exceptionklasse erweitert und den
|
|
<link linkend="language.oop5.decon">Konstruktor</link> neu definiert, ist
|
|
es dringend empfohlen, dass der Konstruktor der Klasse
|
|
<link linkend="language.oop5.paamayim-nekudotayim">parent::__construct()</link>
|
|
aufruft, um sicherzustellen, dass alle verfügbaren Daten korrekt zugewiesen
|
|
wurden. Die
|
|
<link linkend="language.oop5.magic">__toString()</link>-Methode kann
|
|
überschrieben werden, um eine benutzerdefinierte Ausgabe anzubieten, wenn
|
|
das Objekt durch eine Zeichenkette repräsentiert werden soll.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
Exceptions können nicht geklont werden. Der Versuch, eine Exception zu
|
|
<link linkend="language.oop5.cloning">klonen</link>, wird einen fatalen
|
|
<constant>E_ERROR</constant>-Fehler zur Folge haben.
|
|
</para>
|
|
</note>
|
|
<example>
|
|
<title>Die Exceptionklasse erweitern</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/**
|
|
* Eine eigene Exceptionklasse definieren
|
|
*/
|
|
class MyException extends Exception
|
|
{
|
|
// Die Exception neu definieren, damit die Mitteilung nicht optional ist
|
|
public function __construct($message, $code = 0, ?Throwable $previous = null) {
|
|
// etwas Code
|
|
|
|
// sicherstellen, dass alles korrekt zugewiesen wird
|
|
parent::__construct($message, $code, $previous);
|
|
}
|
|
|
|
// benutzerdefinierte Stringdarstellung des Objektes
|
|
public function __toString() {
|
|
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
|
|
}
|
|
|
|
public function customFunction() {
|
|
echo "Eine eigene Funktion für diesen Exceptiontyp\n";
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Erzeuge eine Klasse, um die Exception zu testen
|
|
*/
|
|
class TestException
|
|
{
|
|
public $var;
|
|
|
|
const THROW_NONE = 0;
|
|
const THROW_CUSTOM = 1;
|
|
const THROW_DEFAULT = 2;
|
|
|
|
function __construct($avalue = self::THROW_NONE) {
|
|
|
|
switch ($avalue) {
|
|
case self::THROW_CUSTOM:
|
|
// eigene Exception werfen
|
|
throw new MyException('1 ist ein ungültiger Parameter', 5);
|
|
break;
|
|
|
|
case self::THROW_DEFAULT:
|
|
// Vorgabe werfen
|
|
throw new Exception('2 ist kein zugelassener Parameter', 6);
|
|
break;
|
|
|
|
default:
|
|
// Keine Exception; das Objekt wird erzeugt
|
|
$this->var = $avalue;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Beispiel 1
|
|
try {
|
|
$o = new TestException(TestException::THROW_CUSTOM);
|
|
} catch (MyException $e) { // Wird gefangen
|
|
echo "Meine Exception gefangen\n", $e;
|
|
$e->customFunction();
|
|
} catch (Exception $e) { // Übersprungen
|
|
echo "Standardexception gefangen\n", $e;
|
|
}
|
|
|
|
// Ausführung fortsetzen
|
|
var_dump($o); // Null
|
|
echo "\n\n";
|
|
|
|
|
|
// Beispiel 2
|
|
try {
|
|
$o = new TestException(TestException::THROW_DEFAULT);
|
|
} catch (MyException $e) { // Dieser Typ passt nicht
|
|
echo "Meine Exception gefangen\n", $e;
|
|
$e->customFunction();
|
|
} catch (Exception $e) { // Wird gefangen
|
|
echo "Standardexception gefangen\n", $e;
|
|
}
|
|
|
|
// Ausführung fortsetzen
|
|
var_dump($o); // Null
|
|
echo "\n\n";
|
|
|
|
|
|
// Beispiel 3
|
|
try {
|
|
$o = new TestException(TestException::THROW_CUSTOM);
|
|
} catch (Exception $e) { // Wird gefangen
|
|
echo "Standardexception gefangen\n", $e;
|
|
}
|
|
|
|
// Ausführung fortsetzen
|
|
var_dump($o); // Null
|
|
echo "\n\n";
|
|
|
|
|
|
// Beispiel 4
|
|
try {
|
|
$o = new TestException();
|
|
} catch (Exception $e) { // Übersprungen, keine Exception geworfen
|
|
echo "Standardexception gefangen\n", $e;
|
|
}
|
|
|
|
// Ausführung fortsetzen
|
|
var_dump($o); // TestException
|
|
echo "\n\n";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|