PHP Error Handling Exceptions
PHP Error Handling: Exceptions richtig nutzen
Gutes Error Handling macht den Unterschied zwischen robuster Software und Chaos. Lernen Sie, wie Sie Exceptions in PHP richtig einsetzen.
Exception Grundlagen
Exception werfen
<?php
function divide(int $a, int $b): float
{
if ($b === 0) {
throw new InvalidArgumentException('Division durch Null nicht erlaubt');
}
return $a / $b;
}
Exception fangen
<?php
try {
$result = divide(10, 0);
echo "Ergebnis: $result";
} catch (InvalidArgumentException $e) {
echo "Fehler: " . $e->getMessage();
} catch (Exception $e) {
echo "Unerwarteter Fehler: " . $e->getMessage();
} finally {
// Wird IMMER ausgeführt
echo "Fertig!";
}
Exception-Hierarchie
Throwable (Interface)
├── Error (PHP-interne Fehler)
│ ├── TypeError
│ ├── ArgumentCountError
│ └── ...
└── Exception (Anwendungs-Exceptions)
├── LogicException
│ ├── InvalidArgumentException
│ ├── OutOfRangeException
│ └── ...
└── RuntimeException
├── OutOfBoundsException
├── UnexpectedValueException
└── ...
Eigene Exception-Klassen
<?php
namespace App\Exception;
class ValidationException extends \Exception
{
private array $errors;
public function __construct(array $errors, string $message = 'Validierung fehlgeschlagen')
{
parent::__construct($message);
$this->errors = $errors;
}
public function getErrors(): array
{
return $this->errors;
}
}
class UserNotFoundException extends \RuntimeException
{
public function __construct(int $userId)
{
parent::__construct("User mit ID $userId nicht gefunden");
}
}
class DatabaseException extends \RuntimeException {}
class AuthenticationException extends \RuntimeException {}
Verwendung
<?php
use App\Exception\ValidationException;
use App\Exception\UserNotFoundException;
function createUser(array $data): User
{
$errors = [];
if (empty($data['email'])) {
$errors['email'] = 'E-Mail ist erforderlich';
}
if (empty($data['password'])) {
$errors['password'] = 'Passwort ist erforderlich';
}
if (!empty($errors)) {
throw new ValidationException($errors);
}
return new User($data);
}
try {
$user = createUser($_POST);
} catch (ValidationException $e) {
$errors = $e->getErrors();
// Fehler an Template übergeben
}
Exception mit Kontext
<?php
try {
$user = $repository->find($id);
} catch (DatabaseException $e) {
// Ursprüngliche Exception als "Previous" weitergeben
throw new UserNotFoundException($id, $e);
}
// Exception-Kette auslesen
catch (UserNotFoundException $e) {
echo $e->getMessage();
echo $e->getPrevious()?->getMessage(); // Ursprünglicher Fehler
}
Error Handler für nicht gefangene Exceptions
<?php
set_exception_handler(function (Throwable $e): void {
error_log($e->getMessage() . "\n" . $e->getTraceAsString());
if (getenv('APP_DEBUG') === 'true') {
echo "<pre>";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo $e->getTraceAsString();
echo "</pre>";
} else {
http_response_code(500);
echo "Ein Fehler ist aufgetreten.";
}
});
PHP Errors zu Exceptions konvertieren
<?php
set_error_handler(function (
int $severity,
string $message,
string $file,
int $line
): bool {
// Error als Exception werfen
throw new ErrorException($message, 0, $severity, $file, $line);
});
// Jetzt werden auch Warnings zu Exceptions
try {
$content = file_get_contents('nicht-vorhanden.txt');
} catch (ErrorException $e) {
echo "Datei konnte nicht gelesen werden";
}
API Error Response
<?php
class ApiController
{
public function handle(): void
{
try {
$result = $this->processRequest();
$this->jsonResponse($result);
} catch (ValidationException $e) {
$this->jsonError(422, 'Validierungsfehler', $e->getErrors());
} catch (AuthenticationException $e) {
$this->jsonError(401, 'Nicht authentifiziert');
} catch (UserNotFoundException $e) {
$this->jsonError(404, $e->getMessage());
} catch (Throwable $e) {
error_log($e->getMessage());
$this->jsonError(500, 'Interner Serverfehler');
}
}
private function jsonError(int $status, string $message, array $details = []): void
{
http_response_code($status);
header('Content-Type: application/json');
echo json_encode([
'error' => true,
'message' => $message,
'details' => $details ?: null
]);
}
}
Best Practices
💡 Empfehlungen:
- Spezifische Exceptions werfen, nicht nur
Exception - Exceptions früh werfen, spät fangen
- Kontext in Exception-Message geben
- Nie leere catch-Blöcke (mindestens loggen)
- In Produktion: keine Stack-Traces anzeigen
- Exception für außergewöhnliche Fälle, nicht für Kontrollfluss
Was NICHT tun
❌ Anti-Patterns:
// ❌ Leerer catch-Block
try {
riskyOperation();
} catch (Exception $e) {
// Fehler ignorieren - SCHLECHT!
}
// ❌ Pokemon Exception Handling
try {
everything();
} catch (Exception $e) {
// Gotta catch 'em all - aber was tun damit?
}
// ❌ Exception für Kontrollfluss
try {
$user = findUser($id);
} catch (UserNotFoundException $e) {
// Besser: if ($user === null)
}