Practical examples of implementing error handling in PHP
Real-world examples of implementing error handling in PHP
Let’s start with concrete scenarios. These examples of implementing error handling in PHP are the patterns you’ll actually copy into your codebase. We’ll move from basic to more advanced, but all of them are production-grade ideas, not toy snippets.
Example of centralized error and exception handling
A common pattern is to route all errors and exceptions through a single handler. That gives you one place to log, format, and respond.
<?php
// public/index.php
declare(strict_types=1);
// Show detailed errors only in development
\(isDev = (\)_ENV['APP_ENV'] ?? 'prod') === 'dev';
ini_set('display_errors', $isDev ? '1' : '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/../storage/logs/php-error.log');
error_reporting(E_ALL);
function handleError(int \(severity, string \)message, string \(file, int \)line): bool
{
// Convert all errors to ErrorException so we can catch them
throw new ErrorException(\(message, 0, \)severity, \(file, \)line);
}
function handleException(Throwable $e): void
{
global $isDev;
error_log(sprintf(
"[UNCAUGHT] %s: %s in %s on line %d\nStack trace:\n%s\n",
get_class($e),
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
));
http_response_code(500);
if ($isDev) {
echo '<h1>Application error</h1>';
echo '<pre>' . htmlspecialchars((string)$e) . '</pre>';
} else {
echo 'Something went wrong. Please try again later.';
}
}
set_error_handler('handleError');
set_exception_handler('handleException');
// From here on, any warning/notice becomes an exception you can catch
This is one of the best examples of taking PHP’s loose error model and turning it into something predictable. By converting all errors into ErrorException, you can use try/catch everywhere instead of juggling error_get_last() and return codes.
Examples of handling database errors with PDO exceptions
Database failures are a classic source of white screens. Here’s an example of using PDO with exception-based error handling.
<?php
$dsn = 'mysql:host=localhost;dbname=app;charset=utf8mb4';
$user = 'app_user';
$pass = 'secret';
try {
\(pdo = new PDO(\)dsn, \(user, \)pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
\(stmt = \)pdo->prepare('SELECT * FROM users WHERE email = :email');
\(stmt->execute(['email' => \)inputEmail]);
\(user = \)stmt->fetch();
} catch (PDOException $e) {
// Log detailed DB error
error_log('DB error: ' . $e->getMessage());
// Return safe message to caller
http_response_code(500);
echo 'We are having trouble accessing your account data. Please try again later.';
}
In production, this pattern protects you from leaking SQL details while still capturing enough data in logs to debug. In 2024, almost every serious PHP stack that talks to a database uses this style of error handling.
Best examples of validating user input and throwing domain-specific exceptions
Raw if checks with die() calls are still everywhere in legacy PHP. A better approach: wrap validation failures in domain-specific exceptions. Here’s an example of implementing error handling in PHP for an email registration flow.
<?php
class ValidationException extends RuntimeException
{
private array $errors;
public function __construct(array $errors)
{
parent::__construct('Validation failed');
\(this->errors = \)errors;
}
public function getErrors(): array
{
return $this->errors;
}
}
function validateRegistration(array $data): void
{
$errors = [];
if (empty(\(data['email']) || !filter_var(\)data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Please provide a valid email address.';
}
if (empty(\(data['password']) || strlen(\)data['password']) < 12) {
$errors['password'] = 'Password must be at least 12 characters.';
}
if ($errors) {
throw new ValidationException($errors);
}
}
try {
validateRegistration($_POST);
// Continue with registration
} catch (ValidationException $e) {
http_response_code(422);
header('Content-Type: application/json');
echo json_encode([
'message' => $e->getMessage(),
'errors' => $e->getErrors(),
]);
}
This is one of those real examples that scales nicely: you can reuse ValidationException across forms, APIs, and background jobs.
Examples include handling third‑party API timeouts and network failures
External APIs fail. DNS breaks. Timeouts happen. Ignoring those errors in PHP is how you end up with stuck orders or half-finished workflows.
Here’s an example of implementing error handling in PHP for an HTTP call using curl with timeouts and structured responses.
<?php
class ApiException extends RuntimeException {}
function callPaymentApi(array $payload): array
{
$ch = curl_init('https://payments.example.com/charge');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_TIMEOUT => 10, // seconds
CURLOPT_CONNECTTIMEOUT => 5,
]);
\(responseBody = curl_exec(\)ch);
if ($responseBody === false) {
\(error = curl_error(\)ch);
curl_close($ch);
throw new ApiException('Payment API request failed: ' . $error);
}
\(statusCode = curl_getinfo(\)ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($statusCode >= 500) {
throw new ApiException('Payment API unavailable, status ' . $statusCode);
}
\(data = json_decode(\)responseBody, true, 512, JSON_THROW_ON_ERROR);
if (($data['status'] ?? null) !== 'success') {
throw new ApiException('Payment declined: ' . ($data['message'] ?? 'Unknown error'));
}
return $data;
}
try {
$result = callPaymentApi(['amount' => 1000, 'currency' => 'USD']);
// Mark order as paid
} catch (ApiException | JsonException $e) {
error_log('Payment error: ' . $e->getMessage());
http_response_code(502);
echo 'We could not process your payment. Please try again or use another method.';
}
This is one of the best examples of layering concerns: callPaymentApi knows about HTTP and JSON; the caller decides how to respond to the user.
Example of logging errors with Monolog in a modern stack
By 2024, many PHP apps run under frameworks like Laravel or Symfony, which use Monolog for logging. You can still use the same pattern in a plain PHP app.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
require __DIR__ . '/vendor/autoload.php';
$log = new Logger('app');
$log->pushHandler(new StreamHandler(__DIR__ . '/storage/logs/app.log', Logger::WARNING));
set_exception_handler(function (Throwable \(e) use (\)log) {
$log->error('Uncaught exception', [
'type' => get_class($e),
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
]);
http_response_code(500);
echo 'Internal server error.';
});
// Somewhere later
try {
// Risky code
} catch (Throwable $e) {
\(log->warning('Handled exception', ['exception' => \)e]);
}
This example of implementing error handling in PHP with Monolog gives you structured logs that work well with modern observability stacks like ELK or OpenSearch.
Examples of handling file upload errors in PHP
File uploads are another area where people forget to handle errors. PHP already gives you error codes in $_FILES — you just have to use them.
<?php
function handleProfileUpload(array $file): void
{
if (!isset(\(file['error']) || is_array(\)file['error'])) {
throw new RuntimeException('Invalid upload parameters.');
}
switch ($file['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Uploaded file is too large.');
default:
throw new RuntimeException('Unknown upload error.');
}
if ($file['size'] > 5 * 1024 * 1024) { // 5 MB
throw new RuntimeException('Uploaded file exceeds 5MB limit.');
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
\(mime = \)finfo->file($file['tmp_name']);
$allowed = [
'jpg' => 'image/jpeg',
'png' => 'image/png',
];
\(ext = array_search(\)mime, $allowed, true);
if ($ext === false) {
throw new RuntimeException('Invalid file format.');
}
\(target = sprintf('uploads/%s.%s', bin2hex(random_bytes(8)), \)ext);
if (!move_uploaded_file(\(file['tmp_name'], \)target)) {
throw new RuntimeException('Failed to move uploaded file.');
}
}
try {
handleProfileUpload($_FILES['avatar']);
echo 'Upload successful.';
} catch (RuntimeException $e) {
error_log('Upload error: ' . $e->getMessage());
echo $e->getMessage();
}
This is one of those real examples where careful error handling improves both security and user experience.
Example of catching fatal errors and logging shutdown issues
Some errors in PHP are fatal and would normally kill your script instantly. But you can still catch the last error on shutdown and log it.
<?php
register_shutdown_function(function () {
$error = error_get_last();
if (\(error !== null && in_array(\)error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
error_log(sprintf(
'[FATAL] %s in %s on line %d',
$error['message'],
$error['file'],
$error['line']
));
// Optionally show a generic message to the user
if (!headers_sent()) {
http_response_code(500);
}
echo 'Unexpected server error. Please try again later.';
}
});
When you review logs, these fatal error entries often point directly to the bug that needs fixing.
Example of separating development and production error behavior
One of the best examples of modern PHP practice is to treat development and production very differently. In development you want noisy, visible errors. In production you want quiet logs and friendly messages.
<?php
\(isDev = (\)_ENV['APP_ENV'] ?? 'prod') === 'dev';
if ($isDev) {
ini_set('display_errors', '1');
error_reporting(E_ALL);
} else {
ini_set('display_errors', '0');
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT);
}
You can combine this with the earlier centralized handler examples of implementing error handling in PHP to get consistent behavior across environments.
Trends in PHP error handling for 2024–2025
A few patterns have become standard in the PHP ecosystem:
- Exceptions over error codes: Modern libraries throw exceptions by default (PDO, many HTTP clients, modern ORMs). Your code should expect and catch them.
- Typed exceptions: Instead of throwing
Exceptioneverywhere, developers use domain-specific types likeValidationException,ApiException, andDomainExceptionto makecatchblocks more precise. - Structured logging: Tools like Monolog and centralized log aggregation are common even in mid-size apps.
- Health monitoring: Many teams pair PHP error logs with uptime monitors and health checks. For general guidance on error reporting and monitoring culture, resources from organizations like NIST and universities such as MIT provide good background on reliability practices.
If your codebase still relies heavily on @ to suppress warnings or die() inside business logic, these examples of implementing error handling in PHP give you a modern alternative.
FAQ: common questions and short examples
What are some simple examples of PHP error handling for beginners?
A very small example of error handling is wrapping risky code in try/catch:
try {
echo 10 / 0; // Will trigger a warning converted to ErrorException if you use set_error_handler
} catch (Throwable $e) {
error_log($e->getMessage());
echo 'Math error.';
}
Pair this with set_error_handler from the earlier section and you have a simple, consistent pattern.
Can you show an example of handling JSON errors in PHP?
Yes. With JSON_THROW_ON_ERROR, you can treat bad JSON as an exception instead of checking json_last_error().
<?php
\(json = \)_POST['payload'] ?? '';
try {
\(data = json_decode(\)json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
http_response_code(400);
echo 'Invalid JSON payload.';
exit;
}
This pattern fits nicely with the other examples of implementing error handling in PHP shown above.
How do I avoid exposing sensitive error details to users?
Use environment-aware behavior: show stack traces only in development, log details in production, and return generic messages to users. The centralized handler example earlier demonstrates this. For general secure coding guidance, the OWASP Foundation maintains widely respected recommendations that align well with these PHP practices.
Are there examples of integrating PHP error handling with monitoring tools?
Most monitoring platforms (Sentry, New Relic, etc.) provide PHP SDKs that hook into set_exception_handler and register_shutdown_function. You keep your local logging, and the SDK forwards errors to the external service. The patterns in the examples above still apply; you just call the SDK’s capture function inside your handlers.
If you treat these snippets as building blocks rather than isolated tricks, you’ll end up with a consistent, predictable error handling strategy. That’s the difference between “it works on my machine” and “it behaves the same way every time it fails.”
Related Topics
Real‑world examples of using Composer for PHP dependency management
Real-world examples of hosting open source SaaS with PHP
The best examples of simple PHP function examples for beginners
The best examples of creating a PHP session: 3 practical examples for real projects
The best examples of learn PHP arrays and loops with practical examples
Real‑world examples of practical PHP date and time functions examples
Explore More PHP Code Snippets
Discover more examples and insights in this category.
View All PHP Code Snippets