PHP: Error Handling and Debugging
After 32 lessons of writing code, you've undoubtedly stumbled into your share of pitfalls—blank white pages, inscrutable error messages, and not knowing how to use try/catch. This lesson ties all debugging skills together, upgrading you from "finding bugs by luck" to "systematic troubleshooting."
1. Error Levels
PHP has three types of errors:
TEXT
📖 参照専用
Notice ← "You may have overlooked something" (undefined variable, offset)
Warning ← "The code has a problem but can continue" (include file not found)
Fatal Error ← "The code has a serious error and must stop" (calling a non-existent function)
PHP
<?php
// Notice: undefined variable (PHP continues execution)
echo $undefinedVar; // Notice: Undefined variable $undefinedVar
// Warning: file not found (PHP continues execution)
include 'nonexistent.php'; // Warning: Failed opening...
// Fatal Error: calling a non-existent function (PHP stops)
thisFunctionDoesNotExist(); // Fatal Error: Call to undefined function
?>
2. Controlling Error Reporting
PHP
<?php
// Development: show all errors
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Production: log but don't display
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/php-errors.log');
?>
🔥 Common Mistake: Never set
display_errors = On on a production server—error messages can expose file paths, database passwords, and other sensitive information. Users should see a generic 500 error page; the error details go to the log.
▶ サンプル: Environment-Aware Configuration
PHP
<?php
$isDev = ($_SERVER['SERVER_NAME'] ?? '') === 'localhost';
if ($isDev) {
error_reporting(E_ALL);
ini_set('display_errors', '1');
} else {
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php/errors.log');
}
?>
3. Exception Handling: try/catch/finally
PHP
<?php
function divide(float $a, float $b): float {
if ($b === 0.0) {
throw new Exception("Division by zero is not allowed!");
}
return $a / $b;
}
try {
echo divide(10, 2) . "<br>"; // 5
echo divide(10, 0) . "<br>"; // Throws an exception
} catch (Exception $e) {
echo "[Caught Exception] {$e->getMessage()}<br>";
echo "File: {$e->getFile()}<br>";
echo "Line: {$e->getLine()}<br>";
} finally {
echo "Whether success or failure, finally always runs (e.g., close files, release resources)<br>";
}
?>
| Block | When It Runs |
|---|---|
try |
Execution starts here—exceptions are monitored |
catch |
Runs if an exception is thrown in the try block |
finally |
Always runs regardless (commonly used for cleanup) |
4. Custom Exceptions
PHP
<?php
class ValidationException extends Exception {
private array $errors;
public function __construct(array $errors, string $message = "Validation failed") {
parent::__construct($message);
$this->errors = $errors;
}
public function getErrors(): array {
return $this->errors;
}
}
class UserNotFoundException extends Exception {}
// Using custom exceptions
function findUser(int $id): array {
$users = [1 => ['name' => 'John'], 2 => ['name' => 'Jane']];
if (!isset($users[$id])) {
throw new UserNotFoundException("User ID {$id} does not exist");
}
return $users[$id];
}
function validateUsername(string $name): void {
$errors = [];
if ($name === '') $errors[] = 'Username cannot be empty';
if (strlen($name) > 50) $errors[] = 'Username is too long';
if (!empty($errors)) throw new ValidationException($errors);
}
// Unified exception handling
try {
validateUsername('');
$user = findUser(99);
} catch (ValidationException $e) {
echo "[Validation Failed]<br>" . implode("<br>", $e->getErrors());
} catch (UserNotFoundException $e) {
echo "[User Not Found] {$e->getMessage()}";
} catch (Exception $e) {
echo "[Unknown Error] {$e->getMessage()}";
}
?>
5. Logging
PHP
<?php
class AppLogger {
public function __construct(
private string $logDir
) {
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
}
public function info(string $message, array $context = []): void {
$this->log('INFO', $message, $context);
}
public function error(string $message, array $context = []): void {
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void {
$date = date('Y-m-d');
$time = date('H:i:s');
$line = "[{$time}] [{$level}] {$message}";
if (!empty($context)) {
$line .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
}
$line .= "\n";
$file = "{$this->logDir}/app-{$date}.log";
file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
}
}
$log = new AppLogger(__DIR__ . '/logs');
$log->info('User logged in', ['username' => 'John', 'ip' => $_SERVER['REMOTE_ADDR'] ?? '']);
$log->error('Payment failed', ['order_id' => 12345, 'reason' => 'Insufficient balance']);
?>
6. Debugging Techniques
▶ サンプル: Debugging Utility Functions
PHP
<?php
// Most commonly used debug output
$data = ['name' => 'John', 'skills' => ['PHP', 'MySQL']];
var_dump($data); // Detailed output (type + value)
// array(2) { ["name"]=> string(4) "John" ["skills"]=> ... }
print_r($data); // Concise output
// Array ( [name] => John [skills] => Array ( [0] => PHP [1] => MySQL ) )
// Formatted var_dump (recommended)
echo '<pre>'; print_r($data); echo '</pre>';
// Debug and terminate
var_dump($data); exit; // or die(var_dump($data));
?>
(1) Quick Debugging Reference
PHP
<?php
// Value + type
var_dump($variable);
// Only show in development
if ($isDev) {
echo '<pre>'; var_dump($data); echo '</pre>';
}
// Log it
error_log("Debug info: " . print_r($data, true));
// Check if execution reaches a certain line
echo "HERE"; exit; // or error_log("Reached this line");
// View the call stack
debug_print_backtrace();
// Check if a variable is set
var_dump(isset($var), empty($var));
?>
7. Output Buffering and Custom Error Handlers
PHP
<?php
// Output buffering: capture echo/print output
ob_start();
echo "This text won't be sent directly to the browser.";
$content = ob_get_clean(); // Get and clear the buffer
// $content is now "This text won't be sent directly to the browser."
// Custom error handler
function myErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
$message = "[{$errno}] {$errstr} in {$errfile}:{$errline}";
error_log($message);
if (ini_get('display_errors')) {
echo "<div style='background:#ffebee;padding:10px;margin:10px'>
<strong>PHP Error:</strong> {$message}
</div>";
}
return true; // Prevent PHP's default handling
}
set_error_handler('myErrorHandler');
// Test the custom error handler
echo $undefined; // Now displays in a nice HTML box
?>
❓ よくある質問
Q How do I debug a blank white page?
A The most common cause is a Fatal Error with
display_errors = Off. Troubleshooting steps: (1) add error_reporting(E_ALL); ini_set('display_errors', '1');; (2) check the PHP error log; (3) insert echo "1"; exit; at intervals to find which line stops execution.Q What is Xdebug? Do I need it?
A Xdebug is a professional PHP debugging extension—set breakpoints, step through code, inspect variables. As a beginner,
var_dump is enough. When your project exceeds 1000 lines, it's worth installing. The VS Code + Xdebug combo is as comfortable as browser DevTools.📖 まとめ
- Notice < Warning < Fatal Error—three error levels
- Development:
display_errors On; Production:display_errors Off+log_errors On try { } catch (ExceptionType $e) { } finally { }—the exception handling pattern- Custom exception classes (
class XxxException extends Exception) error_log()for logging,var_dump() + exitfor quick debugging- Blank page troubleshooting: first enable
display_errors, then check the error log - Output buffering
ob_start()/ob_get_clean()controls output
📝 練習問題
- Write a division calculator page that uses try/catch to handle division by zero and display a friendly message.
- Create an
AppExceptionbase class withValidationExceptionandDatabaseExceptionsubclasses. Use them in user registration logic and catch them separately. - Add logging to exercise 2: when exceptions are caught, write error details (timestamp, file, line number) to a log file.