🏠 Home / Hub

🐘 PHP Lesson 13 — Exceptions & Error Handling

← Back to PHP Menu  |  🏠 Hub

1. try / catch / finally

<?php

// Basic exception handling
function divide(float $a, float $b): float {
    if ($b === 0.0) {
        throw new InvalidArgumentException("Cannot divide by zero");
    }
    return $a / $b;
}

try {
    echo divide(10, 2);   // 5.0
    echo divide(10, 0);   // throws!
} catch (InvalidArgumentException $e) {
    echo "Invalid: " . $e->getMessage();  // getMessage()
} finally {
    echo "This ALWAYS runs (cleanup)";  // connection close, etc.
}

// Exception properties
try {
    throw new RuntimeException("Something went wrong", 500);
} catch (RuntimeException $e) {
    echo $e->getMessage();   // "Something went wrong"
    echo $e->getCode();      // 500
    echo $e->getFile();      // /path/to/file.php
    echo $e->getLine();      // line number
    echo $e->getTraceAsString(); // stack trace
}

2. Multiple catch / Exception Hierarchy

<?php

// PHP built-in exception hierarchy
// Throwable
//   ├── Error (PHP errors)
//   │   ├── TypeError
//   │   ├── ArithmeticError
//   │   └── ParseError
//   └── Exception
//       ├── RuntimeException
//       ├── LogicException
//       │   ├── InvalidArgumentException
//       │   ├── BadMethodCallException
//       │   └── OutOfRangeException
//       └── ...many more

function processUser(int $id): array {
    if ($id <= 0) {
        throw new InvalidArgumentException("ID must be positive, got: $id");
    }
    if ($id > 9999) {
        throw new RuntimeException("User not found: $id");
    }
    return ['id' => $id, 'name' => 'Ko Ko'];
}

// Multiple catches — specific first, general last
try {
    $user = processUser(-1);
} catch (InvalidArgumentException $e) {
    echo "Validation: " . $e->getMessage();
} catch (RuntimeException $e) {
    echo "Runtime: " . $e->getMessage();
} catch (Exception $e) {
    echo "General: " . $e->getMessage();
} finally {
    echo "Done";
}

// Catch multiple types (PHP 8+)
try {
    processUser(0);
} catch (InvalidArgumentException | OutOfRangeException $e) {
    echo "Input error: " . $e->getMessage();
}

3. Custom Exception Classes

<?php

namespace App\Exceptions;

// Base app exception
class AppException extends \RuntimeException {
    public function __construct(
        string $message,
        private int $statusCode = 500,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $statusCode, $previous);
    }

    public function getStatusCode(): int { return $this->statusCode; }

    public function toArray(): array {
        return [
            'error'   => $this->getMessage(),
            'code'    => $this->statusCode,
            'type'    => get_class($this),
        ];
    }
}

class ValidationException extends AppException {
    private array $errors = [];

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

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

    public function toArray(): array {
        return array_merge(parent::toArray(), ['errors' => $this->errors]);
    }
}

class NotFoundException extends AppException {
    public function __construct(string $resource, int|string $id) {
        parent::__construct("$resource with ID '$id' not found", 404);
    }
}

class AuthException extends AppException {
    public function __construct(string $message = "Unauthorized") {
        parent::__construct($message, 401);
    }
}

// Usage
try {
    $errors = ['email' => 'Invalid email', 'name' => 'Required'];
    throw new ValidationException($errors);
} catch (ValidationException $e) {
    print_r($e->toArray());
    // ['error' => 'Validation failed', 'code' => 422, 'errors' => [...]]
}

try {
    throw new NotFoundException("User", 999);
} catch (NotFoundException $e) {
    echo $e->getMessage();   // "User with ID '999' not found"
    echo $e->getStatusCode(); // 404
}

4. Exception Chaining & Re-throwing

<?php

function connectToDatabase(): \PDO {
    try {
        return new \PDO("mysql:host=invalid_host", "user", "pass");
    } catch (\PDOException $e) {
        // Chain: preserve original exception
        throw new \RuntimeException(
            "Database connection failed: " . $e->getMessage(),
            500,
            $e   // ← previous exception
        );
    }
}

try {
    connectToDatabase();
} catch (\RuntimeException $e) {
    echo $e->getMessage();          // "Database connection failed: ..."
    echo $e->getPrevious()?->getMessage(); // original PDOException message
}

// Re-throwing — handle partially, let it bubble
function processRequest(array $data): array {
    try {
        // do work...
        if (empty($data['id'])) throw new \InvalidArgumentException("Missing id");
        return ['ok' => true];
    } catch (\InvalidArgumentException $e) {
        // log it
        error_log("Request error: " . $e->getMessage());
        throw $e;  // re-throw to caller
    }
}

5. Global Error Handler

<?php

// Global exception handler — unhandled exceptions ကို catch
set_exception_handler(function(\Throwable $e) {
    $statusCode = method_exists($e, 'getStatusCode')
        ? $e->getStatusCode() : 500;

    http_response_code($statusCode);
    header('Content-Type: application/json');

    echo json_encode([
        'error'   => $e->getMessage(),
        'code'    => $statusCode,
        'type'    => get_class($e),
    ]);

    // log to file
    error_log(date('Y-m-d H:i:s') . " [{$e->getCode()}] {$e->getMessage()}\n", 3, 'logs/error.log');
});

// Global error handler
set_error_handler(function(int $errno, string $errstr, string $file, int $line) {
    if (!(error_reporting() & $errno)) return false;
    throw new \ErrorException($errstr, 0, $errno, $file, $line);
});

// PHP.ini settings (development)
// error_reporting = E_ALL
// display_errors  = On
// log_errors      = On
// error_log       = /path/to/php_error.log

// PHP.ini settings (production)
// display_errors  = Off   ← never show errors to users!
// log_errors      = On
// error_log       = /path/to/php_error.log
💡 Production: display_errors=Off ကို မမေ့ပါနဲ့! Error messages မှာ sensitive data ပါနိုင်တယ်

🎉 PHP Complete!

Syntax → Conditions → Loops → Functions → Arrays → Strings → Forms → OOP Classes → Inheritance → Interfaces → Traits → Namespaces → Exceptions

🏠 Hub 🐘 PHP Menu

← PHP 12  |  🏠 Back to Hub

📌 Study Checklist