Why PHP Error Handling Matters
Every PHP developer has experienced it: you deploy your code, a user reports a blank white page, and you have absolutely no idea what went wrong. The page just... stops rendering. No error message, no stack trace, nothing. This is the infamous "White Screen of Death," and it happens because PHP's error handling was not configured correctly.
Proper error handling is the difference between a developer who can diagnose and fix issues in minutes and one who spends hours guessing. In development, you want every error screaming at you in the browser. In production, you want errors silently logged to files where you can review them, while users see a friendly error page instead of raw stack traces that expose your code structure.
This guide covers everything you need to know about PHP error handling: the error reporting levels, the critical display_errors vs log_errors settings, custom error and exception handlers, structured logging, and the tools and techniques that make debugging PHP applications efficient.
PHP Error Reporting Levels
PHP categorizes errors into different levels, each represented by a predefined constant. Understanding these levels is essential for knowing what gets reported and what gets silently ignored.
| Constant | Value | Description | Severity |
|---|---|---|---|
E_ERROR | 1 | Fatal runtime error, script halts | Fatal |
E_WARNING | 2 | Non-fatal runtime warning | Warning |
E_PARSE | 4 | Syntax error at compile time | Fatal |
E_NOTICE | 8 | Minor issue (undefined variable, etc.) | Notice |
E_STRICT | 2048 | Suggestions for code improvement | Notice |
E_DEPRECATED | 8192 | Code that will break in future PHP versions | Warning |
E_ALL | ~0 | All errors, warnings, and notices | All |
The error_reporting directive accepts a bitmask of these constants. You combine them with bitwise operators:
E_ALL during development. In production, use E_ALL as well but with display_errors = Off and log_errors = On. Never suppress error reporting — you want to know about every issue, even if users do not see them.
The Two Critical Settings: display_errors vs log_errors
These two php.ini directives are the most important settings for error handling, and they serve completely different purposes:
display_errors
Controls whether errors are shown in the browser output. In development, set this to On so you see errors immediately. In production, set this to Off — always. Displaying errors to users exposes file paths, database credentials, and code structure.
Dev: On Prod: Off
log_errors
Controls whether errors are written to a log file. This should be On in both environments. Combined with the error_log directive, this ensures every error is captured somewhere you can review it.
Dev: On Prod: On
Configuring php.ini: Development vs Production
Here are the recommended php.ini settings for each environment:
Development Configuration
Production Configuration
error_reporting, display_errors, and log_errors through the panel GUI. Each user's PHP-FPM pool runs isolated, so one user's error settings never affect another's.
Setting the Error Log Location
The error_log directive specifies where PHP writes its error logs. You can set it to a file path or use syslog to integrate with your system's logging infrastructure.
Make sure the directory exists and is writable by the PHP-FPM process user. A common mistake is setting the error log to a path that does not exist or that the web server user cannot write to — PHP silently fails to log anything.
Runtime Error Configuration
While php.ini settings are the primary configuration method, you can also set error handling at runtime in your code. This is useful for temporary debugging or for application-specific error handling:
display_startup_errors cannot be changed at runtime because startup errors occur before your script runs. These must be set in php.ini or the PHP-FPM pool configuration.
Custom Error Handlers
PHP lets you register custom functions to handle errors and exceptions. This is powerful for implementing structured logging, sending error notifications, or integrating with error tracking services like Sentry or Bugsnag.
set_error_handler()
This function registers a callback that intercepts all non-fatal errors (warnings, notices, deprecations). Fatal errors (E_ERROR, E_PARSE) cannot be caught by set_error_handler() — use register_shutdown_function() for those.
set_exception_handler()
This catches any uncaught exception that bubbles up to the top level without being caught by a try/catch block:
register_shutdown_function()
This is your last line of defense. It runs when the script terminates, including after fatal errors. Use it to catch the errors that set_error_handler() cannot:
Try/Catch/Finally: Structured Exception Handling
Modern PHP (7.0+) uses exceptions as the primary error handling mechanism. The try/catch/finally pattern gives you fine-grained control over how your application responds to different types of errors:
catch without a variable when you do not need the exception object: catch (SomeException) instead of catch (SomeException $e). This is useful when you just want to handle the error type without inspecting the details.
PHP Error Types: A Practical Guide
Here are the most common PHP errors you will encounter, what causes them, and how to fix them:
Undefined Variable / Undefined Array Key E_WARNING
Cause: Accessing a variable or array key that has not been set.
Fix: Use the null coalescing operator: $value = $array['key'] ?? 'default'; or isset() checks.
Cannot redeclare function E_ERROR
Cause: Including a file with function definitions multiple times.
Fix: Use require_once or include_once instead of require or include. Better yet, use autoloading with Composer.
Allowed memory size exhausted E_ERROR
Cause: Script uses more memory than memory_limit allows.
Fix: Increase memory_limit in php.ini or optimize code (use generators, process data in chunks, free unused variables with unset()).
Maximum execution time exceeded E_ERROR
Cause: Script runs longer than max_execution_time.
Fix: Increase the timeout for long-running tasks: set_time_limit(300); or optimize the code. For truly long tasks, use CLI scripts or background queues.
Setting Up Xdebug for Debugging
Xdebug is the essential PHP debugging extension. It provides step-by-step debugging, stack traces, profiling, and code coverage analysis. Here is how to set it up:
Log Rotation: Preventing Disk Full Disasters
PHP error logs can grow to gigabytes if left unchecked. Set up log rotation to keep them manageable:
This configuration rotates logs daily, keeps 14 days of history, compresses old logs, and sends a reload signal to PHP-FPM so it starts writing to the new file.
Structured Logging with Monolog
For production applications, structured logging with Monolog (PSR-3 compatible) is far superior to raw error_log() calls. Monolog supports multiple output handlers, log levels, and formatters:
JSON-formatted logs are searchable, parseable, and can be ingested by centralized logging systems like the ELK stack (Elasticsearch, Logstash, Kibana) or Grafana Loki.
Common PHP Errors and Fixes
Here is a quick-reference table of the errors developers encounter most frequently:
| Error | Cause | Fix |
|---|---|---|
| White screen, no error | display_errors=Off + no log_errors | Enable log_errors, check error log |
| Headers already sent | Output before header() call | Move header() before any echo/HTML; check for BOM |
| Class not found | Missing autoload or require | Run composer dump-autoload |
| Permission denied | PHP process cannot write to path | Fix file/directory ownership and permissions |
| Segmentation fault | Extension conflict or PHP bug | Update PHP, disable extensions one by one |
| Undefined constant | Typo in constant name or missing define | Use defined() check, verify spelling |
Error Handling Checklist
Use this checklist when setting up error handling for any PHP project:
error_reporting = E_ALLin both environmentsdisplay_errors = Offin production,Onin developmentlog_errors = Onin both environmentserror_logpoints to a writable directory- Log rotation is configured (logrotate or Monolog's RotatingFileHandler)
- Custom error handler registered with
set_error_handler() - Custom exception handler registered with
set_exception_handler() - Shutdown function catches fatal errors with
register_shutdown_function() - User-friendly error pages displayed for 404 and 500 errors
- Xdebug installed in development only (never production)
- Sensitive data (passwords, API keys) never logged in plain text
- Error monitoring/alerting configured for critical errors
Wrapping Up
PHP error handling is not glamorous, but it is essential infrastructure that separates amateur deployments from professional ones. The key principles are straightforward: always report all errors, never display them to users in production, always log them to files, use custom handlers for structured logging, and set up log rotation so your disk does not fill up.
The most common mistake is deploying with display_errors = On in production, which exposes your file paths, database connection details, and code structure to anyone who triggers an error. The second most common mistake is deploying with log_errors = Off, which means errors happen silently and you never know about them until users complain.
Get these fundamentals right, add structured logging with Monolog, set up Xdebug in your development environment, and you will be able to diagnose and fix PHP issues faster than you ever thought possible.