©Sergey Emelyanov 2025 | Alle Rechte vorbehalten
Während der Ausführung einer Funktion entsteht oft die Notwendigkeit, einen Fehler zu behandeln und die Ausführung der Funktion abzubrechen. Schauen wir uns an, wie man das in PHP umsetzen kann 🤓
Eine Funktion kann also entweder einen Fehler oder bestimmte Daten zurückgeben. In unserem Beispiel sind die Daten eine ganze Zahl (Integer) und der Fehler ein String.
Im Fehlerfall werfen wir eine Exception.
final class MyException extends Exception {}
/**
* @throws MyException
*/
function calc(): int
{
return 42; // Erfolg
throw new MyException('Ungültig!'); // Fehler
}
try {
$data = calc();
echo sprintf('Ergebnis: %d', $data);
} catch (MyException $e) {
echo sprintf('Fehler: %s', $e->getMessage());
}Wenn ein Fehler ein erwartetes Verhalten ist, handelt es sich nicht um eine Ausnahmesituation, und die Verwendung von Exceptions ist hier inkorrekt: Der Code wird komplizierter, man könnte vergessen, Exceptions zu behandeln, usw.
/**
* @return list{null, string}|list{int, null}
*/
function calc(): array
{
return [42, null]; // Erfolg
return [null, 'Ungültig!']; // Fehler
}
[$data, $error] = calc();
if ($error === null) {
echo sprintf('Ergebnis: %d', $data);
} else {
echo sprintf('Fehler: %s', $error);
}Diese Variante ist schon besser, aber die Verwendung eines Arrays ist nicht sehr komfortabel: komplexe Typ-Annotation und wenig semantischer Code.
Realisierbar für Fälle, in denen sich die Typen von Fehler und Ergebnis unterscheiden.
function calc(): int|string
{
return 42; // Erfolg
return 'Ungültig!'; // Fehler
}
$result = calc();
if (is_int($result)) {
echo sprintf('Ergebnis: %d', $result);
} else {
echo sprintf('Fehler: %s', $result);
}Diese Variante ist nicht immer geeignet, sieht äußerst unsemantisch aus und ist schlecht lesbar.
Das Ergebnis-Objekt enthält den Fehler oder die Daten sowie eine Methode zur Überprüfung des Erfolgs. Beispiel einer Implementierung:
/**
* @template-covariant TResult
* @template-covariant TError
*/
final readonly class Result
{
/**
* @param TResult|TError $data
*/
private function __construct(public mixed $data, private bool $isSuccess) {}
/**
* @template T
* @param T $data
* @return self<T, never>
*/
public static function success(mixed $data): self
{
/** @var self<T, never> */
return new self($data, true);
}
/**
* @template T
* @param T $error
* @return self<never, T>
*/
public static function error(mixed $error): self
{
/** @var self<never, T> */
return new self($error, false);
}
/**
* @phpstan-assert-if-true TResult $this->data
* @phpstan-assert-if-false TError $this->data
*/
public function isSuccess(): bool
{
return $this->isSuccess;
}
}Beispiel für die Verwendung:
/**
* @return Result<int, string>
*/
function calc(): Result
{
return Result::success(42); // Erfolg
return Result::error('Ungültig!'); // Fehler
}
$result = calc();
if ($result->isSuccess()) {
echo sprintf('Ergebnis: %d', $result->data);
} else {
echo sprintf('Fehler: %s', $result->data);
}Meiner Meinung nach ist dies die beste Variante:
©Sergey Emelyanov 2025 | Alle Rechte vorbehalten