PHP Exception Handling
Proper exception handling separates production-ready PHP from hobby projects. Unhandled exceptions expose stack traces to users, hide bugs, and crash applications. Master try-catch-finally and custom exceptions.
Try-Catch-Finally Basics
<?php
function divide(int $a, int $b): float {
if ($b === 0) throw new DivisionByZeroError("Cannot divide by zero");
return $a / $b;
}
try {
echo divide(10, 0);
} catch (DivisionByZeroError $e) {
echo "Math error: " . $e->getMessage();
echo " in " . $e->getFile() . ":" . $e->getLine();
} catch (TypeError $e) {
echo "Type error: " . $e->getMessage();
} catch (\Throwable $e) {
// Catches ALL errors and exceptions
error_log($e->getMessage());
echo "Something went wrong.";
} finally {
// Always runs — close connections, release locks
echo "Done.";
}
Custom Exception Hierarchy
<?php
class AppException extends \RuntimeException {}
class ValidationException extends AppException {
public function __construct(private array $errors) {
parent::__construct("Validation failed");
}
public function getErrors(): array { return $this->errors; }
}
class NotFoundException extends AppException {
public function __construct(string $resource, int $id) {
parent::__construct("$resource with ID $id not found", 404);
}
}
class UnauthorizedException extends AppException {
public function __construct(string $action = "perform this action") {
parent::__construct("Not authorized to $action", 403);
}
}
// Usage
function updatePost(int $id, array $data): void {
if (!postExists($id)) throw new NotFoundException('Post', $id);
if (!currentUserOwns($id)) throw new UnauthorizedException('edit this post');
$errors = validatePostData($data);
if ($errors) throw new ValidationException($errors);
savePost($id, $data);
}
try {
updatePost(42, ['title' => '']);
} catch (ValidationException $e) {
foreach ($e->getErrors() as $field => $msg) echo "$field: $msg\n";
} catch (NotFoundException $e) {
http_response_code(404);
echo json_encode(['error' => $e->getMessage()]);
} catch (UnauthorizedException $e) {
http_response_code(403);
echo json_encode(['error' => $e->getMessage()]);
}
Global Exception Handler
<?php
set_exception_handler(function(\Throwable $e) {
error_log(sprintf("[%s] %s\n%s:%d\n%s",
date('Y-m-d H:i:s'), $e->getMessage(),
$e->getFile(), $e->getLine(), $e->getTraceAsString()
));
http_response_code($e->getCode() >= 400 ? $e->getCode() : 500);
header('Content-Type: application/json');
echo json_encode([
'error' => $e instanceof AppException ? $e->getMessage() : 'Server error',
]);
});
// Convert PHP errors to exceptions
set_error_handler(function(int $sev, string $msg, string $file, int $line) {
throw new \ErrorException($msg, 0, $sev, $file, $line);
});
Q: Difference between Error and Exception in PHP?
Exception handles expected application-level issues (validation, not found). Error represents PHP internals (parse errors, type errors). Both implement Throwable. Use catch(\Throwable) to catch everything in global handlers.
Q: When does finally NOT execute?
Only when PHP process exits via die()/exit() or encounters a fatal that cannot be caught. In all other cases — return inside try, exception thrown, normal flow — finally always runs.
Comments (0)
No comments yet. Be the first!
Leave a Comment