PHP Error Handling

Errors are inevitable in software — files go missing, database connections fail, unexpected values arrive from users. Proper error handling means anticipating these situations and responding in a controlled way rather than letting the application crash or leak sensitive information. PHP provides several mechanisms for handling errors: traditional error levels, exceptions, and custom error handlers.

PHP Error Types

Error TypeSeverityDescription
E_ERRORFatalScript stops immediately (memory exhausted, missing method)
E_WARNINGNon-fatalScript continues but something is wrong (file not found)
E_NOTICEInformationalPossible bug (undefined variable, undefined index)
E_PARSEFatalSyntax error in the code
E_DEPRECATEDInformationalUsing a feature that will be removed in a future PHP version

Controlling Error Display

<?php
  // During development — show all errors
  ini_set("display_errors", 1);
  ini_set("display_startup_errors", 1);
  error_reporting(E_ALL);

  // In production — hide errors from users, log them instead
  // ini_set("display_errors", 0);
  // ini_set("log_errors", 1);
  // ini_set("error_log", "/path/to/error.log");
  // error_reporting(E_ALL);
?>

In production, always log errors rather than displaying them — displaying errors exposes file paths, database names, and other sensitive information to potential attackers.

try / catch / finally — Exception Handling

Exceptions are the modern, object-oriented approach to error handling. Code that might fail is placed inside a try block. If an exception is thrown, execution jumps to the matching catch block. The optional finally block runs regardless of whether an exception occurred.

<?php
  function divideNumbers(float $a, float $b): float {
    if ($b === 0.0) {
      throw new InvalidArgumentException("Division by zero is not allowed.");
    }
    return $a / $b;
  }

  try {
    echo divideNumbers(10, 2) . "\n";   // 5
    echo divideNumbers(10, 0) . "\n";   // Throws exception
  } catch (InvalidArgumentException $e) {
    echo "Error: " . $e->getMessage();
  } finally {
    echo "Calculation attempt finished.";   // Always runs
  }
?>

Catching Multiple Exception Types

<?php
  function processFile(string $filename): string {
    if (!file_exists($filename)) {
      throw new RuntimeException("File not found: $filename");
    }

    $content = file_get_contents($filename);

    if ($content === false || strlen($content) === 0) {
      throw new UnexpectedValueException("File is empty: $filename");
    }

    return $content;
  }

  try {
    $content = processFile("data.txt");
    echo "File content: " . $content;

  } catch (RuntimeException $e) {
    echo "File error: " . $e->getMessage();

  } catch (UnexpectedValueException $e) {
    echo "Content error: " . $e->getMessage();

  } catch (Exception $e) {
    // Catch-all for any other exception
    echo "Unexpected error: " . $e->getMessage();
  }
?>

PHP 8 — Catching Multiple Types in One Catch

<?php
  try {
    // ... code that might throw
  } catch (RuntimeException | InvalidArgumentException $e) {
    echo "Caught: " . $e->getMessage();
  }
?>

Creating Custom Exceptions

Custom exception classes make error handling more specific and meaningful. They extend PHP's built-in Exception class.

<?php
  class DatabaseException extends RuntimeException {
    private string $query;

    public function __construct(string $message, string $query = "", int $code = 0) {
      parent::__construct($message, $code);
      $this->query = $query;
    }

    public function getQuery(): string {
      return $this->query;
    }
  }

  class NotFoundException extends RuntimeException {}

  class ValidationException extends InvalidArgumentException {
    private array $errors;

    public function __construct(array $errors) {
      parent::__construct("Validation failed.");
      $this->errors = $errors;
    }

    public function getErrors(): array {
      return $this->errors;
    }
  }

  // Usage
  try {
    $errors = ['email' => 'Invalid email', 'password' => 'Too short'];
    throw new ValidationException($errors);

  } catch (ValidationException $e) {
    foreach ($e->getErrors() as $field => $message) {
      echo "$field: $message\n";
    }
  }
?>

Custom Error Handler with set_error_handler()

The set_error_handler() function registers a custom function to handle non-fatal PHP errors.

<?php
  function customErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
    $message = "[$errno] $errstr in $errfile on line $errline";
    error_log($message);   // Log the error

    if ($errno === E_ERROR) {
      echo "A critical error occurred. Please try again later.";
      exit(1);
    }

    return true;   // Return true to prevent PHP's default handler from running
  }

  set_error_handler("customErrorHandler");

  // Trigger a notice
  $undefined;   // E_NOTICE
?>

set_exception_handler() — Global Exception Handler

<?php
  function globalExceptionHandler(Throwable $e): void {
    // Log the full details
    error_log($e->getMessage() . "\n" . $e->getTraceAsString());

    // Show a user-friendly message
    http_response_code(500);
    echo "An unexpected error occurred. Our team has been notified.";
  }

  set_exception_handler("globalExceptionHandler");

  // Any uncaught exception will now be handled by this function
  throw new RuntimeException("Something went wrong!");
?>

Key Points

  • PHP has different error types (fatal, warning, notice) with different severity levels.
  • In production, always log errors and hide them from users; in development, display all errors.
  • try / catch / finally is the standard way to handle exceptions in modern PHP.
  • Use throw to raise an exception; catch it with the correct exception class in a catch block.
  • Create custom exception classes by extending Exception or one of its subclasses.
  • Register global handlers with set_error_handler() and set_exception_handler() to catch anything that slips through.
  • The finally block always runs — use it for cleanup operations like closing files or database connections.

Leave a Comment

Your email address will not be published. Required fields are marked *