Friends, today we're going to talk about something that drives anyone who's ever dug through someone else's PHP code up the wall. You open a website, and there's nothing there. A blank screen. No error, no hint of what went wrong.

Sound familiar?

This is the classic case of a PHP fatal error that simply kills the script and disappears into the sunset without saying goodbye. Today, we'll learn how to make PHP talk, enable logs, and find those fatal errors hiding behind the "White Screen of Death."

Why PHP Stays Silent: A Bit of Theory

First, let's understand how PHP handles errors in general. It's not just "error -> log." There's a whole hierarchy.

In PHP 7+, everything that can go wrong implements the Throwable interface. It has two main children: - Error — PHP internal errors (fatal, parse, type errors) - Exception — exceptions that can be caught with try-catch

Fatal errors (E_ERROR, E_PARSE, E_CORE_ERROR) are every developer's worst nightmare. They kill the script instantly, and standard handlers via set_error_handler() won't catch them . The only way to hook into a fatal error is to use register_shutdown_function().

But let's go step by step. First, we need to enable logging so errors actually get written somewhere.

Step 1: Enabling Logging via php.ini

The most proper way to configure logs is to edit the PHP configuration file. Where it's located depends on your system :

| System | Path to php.ini | |---------|----------------| | Ubuntu/Debian (Apache) | /etc/php/7.x/apache2/php.ini | | Ubuntu/Debian (CLI) | /etc/php/7.x/cli/php.ini | | Ubuntu/Debian (PHP-FPM) | /etc/php/7.x/fpm/php.ini | | CentOS/RHEL | /etc/php.ini or /etc/php.d/ | | Windows (XAMPP) | C:\xampp\php\php.ini | | Windows (WAMP) | C:\wamp\bin\php\phpX.X.X\php.ini |

In this file, we're interested in the following directives :

```ini ; Enable writing errors to a log (mandatory!) log_errors = On

; Specify the path to the log file error_log = /var/log/php_errors.log

; Disable displaying errors on screen (for production!) display_errors = Off

; For development, you can enable this, but turn it off later display_startup_errors = Off

; Error reporting level — all errors error_reporting = E_ALL ```

Important: The command-line interface (CLI) uses its own php.ini, and logs might be written to a different location . If you're testing a script in the console, check separately.

After making changes, you need to restart the web server : bash sudo systemctl restart apache2 # for Apache sudo systemctl restart php7.4-fpm # for PHP-FPM

What These Settings Mean

  • log_errors = On — enables writing errors to a file. If you don't do this, PHP will be silent as a grave .
  • error_log — the path to the file where everything will be written. It's crucial that the web server user (usually www-data or apache) has write permissions for this file and its directory .
  • display_errors = Off — definitely turn this off in production to avoid exposing details to users .
  • error_reporting = E_ALL — log all types of errors. For production, you can limit this, but for debugging, it's better to see everything.

Step 2: Alternative: Enabling Logging Directly in Code

If you don't have access to php.ini (e.g., on shared hosting), you can try to enable logging directly in your script using the ini_set() function.

Insert this at the very beginning of the problematic file:

```php

/dev/null ``` Or create a PHP file with `phpinfo();` and look for the **error_log** section. ## Step 4: Reading Logs: Tools and Techniques Once logs are being written, you need to know how to quickly view and filter them. ### tail — Viewing the Latest Events ```bash # Last 50 lines tail -n 50 /var/log/php_errors.log # Follow in real-time (like docker logs -f) tail -f /var/log/php_errors.log ``` ### grep — Searching for Specific Errors ```bash # Only fatal errors grep "Fatal error" /var/log/php_errors.log # Parse errors (syntax errors) grep "Parse error" /var/log/php_errors.log # Ignore case grep -i "warning" /var/log/php_errors.log ``` ### less — Convenient Viewing with Navigation ```bash less /var/log/php_errors.log ``` Inside less, you can press `/` and type a word to search, `n` for the next match, `N` for the previous one. ### Combinations — A Powerful Pipeline ```bash # Show the last 100 lines and search for errors tail -n 100 /var/log/php_errors.log | grep -i fatal # Follow and filter in real-time tail -f /var/log/php_errors.log | grep --line-buffered "error" # Count errors by type grep -o "PHP [A-Za-z]* error" /var/log/php_errors.log | sort | uniq -c ``` ## Step 5: Deciphering Errors: What's What A typical log entry looks like this : ``` [2026-03-04 14:35:22 UTC] PHP Fatal error: Uncaught Error: Call to undefined function foo() in /var/www/html/index.php on line 10 ``` Let's break it down: - **Date and Time** — when it happened - **PHP Fatal error** — the error level - **Uncaught Error: Call to undefined function foo()** — the message - **/var/www/html/index.php:10** — the file and line number Main error levels : | Level | Meaning | Example | |---------|------------|--------| | **E_ERROR** / **Fatal error** | Fatal error, script dies | Calling a non-existent function, out of memory | | **E_WARNING** / **Warning** | Warning, but script continues | `include` of a non-existent file | | **E_PARSE** / **Parse error** | Syntax error, script won't run | Missing semicolon | | **E_NOTICE** / **Notice** | Suggestion, usually not critical | Accessing an undefined variable | | **E_DEPRECATED** | Deprecated construct | Using `mysql_connect()` | ## Step 6: Catching Fatal Errors (The Interesting Part) Fatal errors are the ones that cannot be caught by the standard `set_error_handler()`. They kill the script instantly, and the default handler simply doesn't have time to run . But there is a way! Use `register_shutdown_function()` — this function is called no matter what, even on a fatal error . ### Simple Fatal Error Handler ```php getMessage()." | ".$e->getFile().":".$e->getLine()." | $url\n"; file_put_contents($logFile, $msg, FILE_APPEND); }); // Handler for fatal errors register_shutdown_function(function() use ($logFile) { $error = error_get_last(); if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { $url = ($_SERVER['HTTP_HOST'] ?? 'CLI') . ($_SERVER['REQUEST_URI'] ?? ''); $msg = "[".date('Y-m-d H:i:s')."] [FATAL] ".$error['message']." | ".$error['file'].":".$error['line']." | $url\n"; file_put_contents($logFile, $msg, FILE_APPEND); } }); ``` You can insert such code at the very beginning of your main file (e.g., `index.php` or `header.php`), and it will catch absolutely everything . ## Step 7: Logs in Popular Frameworks If you're using a modern framework, logging is already set up, but the log paths are different. ### Laravel Logs are stored in `storage/logs/laravel.log`. By default, it uses Monolog; the format can be text or JSON. To see errors: ```bash tail -f storage/logs/laravel.log ``` ### Symfony Default logs: `var/log/dev.log` and `var/log/prod.log`. Also uses Monolog, highly configurable. ### WordPress By default, WordPress doesn't write logs, but you can enable them by adding this to `wp-config.php`: ```php define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); ``` Logs will be in `/wp-content/debug.log`. ## Step 8: Common Problems and Their Solutions ### Problem: Logs aren't being written at all What to check: 1. Is `log_errors = On` definitely set? 2. Are there write permissions for the file and its directory? 3. Did you edit the correct php.ini (check via `phpinfo()`)? 4. Did you restart the web server after making changes? ### Problem: Fatal errors aren't being caught Even with logging enabled, fatal errors sometimes aren't written if they occur very early (e.g., a parse error in the very file where you're enabling logging). The only solution is proper placement — at the very beginning, before anything else . ### Problem: Too many logs If `E_ALL` is enabled in production, logs will grow rapidly. Configure `error_reporting` sensibly : ```ini error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE ``` ### Problem: Logs are filling up the disk Set up log rotation with `logrotate`. A config for PHP logs: ``` /var/log/php_errors.log { daily rotate 30 compress delaycompress missingok notifempty create 644 www-data www-data } ``` ## Step 9: Practical Example: Investigating a Blank Page Let's imagine a situation: you open a website, and it's blank. What to do step-by-step: 1. **Check the web server logs** (Apache/Nginx) — often they contain a hint 2. **Check the PHP logs** — look for entries from the last few minutes ```bash tail -n 50 /var/log/php_errors.log | grep -A5 -B5 "$(date +%Y-%m-%d)" ``` 3. **Find the error**: ``` [2026-03-04 15:30:22] PHP Fatal error: Uncaught Error: Class 'DB' not found in /var/www/site/index.php on line 12 ``` 4. **Open the file**, check line 12 — indeed, forgot to include/require the class. 5. **Fix it**, test — the site works! Magic! ## Quick Reference: Cheat Sheet | Action | Command / Code | |----------|---------------| | Enable logging in php.ini | `log_errors = On` + `error_log = /path/to/file` | | Enable in code | `ini_set('log_errors', 'On');` | | View last lines | `tail -f /var/log/php_errors.log` | | Search for fatal errors | `grep "Fatal error" /var/log/php_errors.log` | | Catch fatal errors | `register_shutdown_function()` + `error_get_last()` | | Where are Laravel logs? | `storage/logs/laravel.log` | | Where are Symfony logs? | `var/log/dev.log` and `var/log/prod.log` | ## In Lieu of a Conclusion PHP isn't as scary as it's often made out to be. Yes, the White Screen of Death is terrifying, but behind it is always a specific error that just doesn't want to speak up. Learn to enable logs, and PHP will start talking. The main rules: - In production, **always** enable `log_errors` and disable `display_errors` - Write logs to a dedicated file, not to syslog — it's easier to search - Use `register_shutdown_function` to catch fatal errors - Check your logs regularly — at least once a day And remember: perfect code doesn't write errors. But if an error does happen, at least let it leave a note. ?>