DALT.PHP
Framework Deep Dive1. Entry Point

Error Handling

How validation errors and exceptions are caught and handled gracefully

Catching Errors Gracefully

After routing, the framework wraps everything in a try-catch block to handle errors elegantly.

Think of it like a safety net:

  • If something goes wrong, catch it
  • Show a helpful message
  • Don't crash the entire app

The Try-Catch Block

try {
    $router->route($uri, $method, $request);
} catch (ValidationException $exception) {
    // Handle form validation errors
} catch (\Throwable $e) {
    // Handle all other errors
}

Two types of errors are handled differently:

  1. Validation errors - Expected, user-fixable
  2. Other errors - Unexpected, developer needs to fix

Handling Validation Errors

catch (ValidationException $exception) {
    Session::flash('errors', $exception->errors);
    Session::flash('old', $exception->old);
    redirect($router->previousUrl());
}

What is a ValidationException?

When a form has invalid input, controllers throw this special exception:

// In a controller
if (!Validator::email($email)) {
    ValidationException::throw(
        ['email' => 'Invalid email format'],
        ['email' => $email]
    );
}

The Flow

  1. User submits form with bad data
  2. Controller validates and throws ValidationException
  3. Entry point catches it here
  4. Errors stored in flash (temporary session data)
  5. Old input stored in flash (so user doesn't retype everything)
  6. Redirect back to the form page

Why Flash?

Flash data exists for exactly one request:

// Request 1: Form submission fails
Session::flash('errors', ['email' => 'Invalid']);
redirect('/register');

// Request 2: Show form again
$errors = Session::get('errors');  // Available
echo $errors['email'];

// Request 3: Any other page
$errors = Session::get('errors');  // Gone (cleaned up)

This prevents errors from showing up on unrelated pages.

The User Experience

Without this handling:

Fatal error: Uncaught ValidationException
Stack trace: ...

With this handling:

[User sees the form again]
❌ Invalid email format
[Email field still has their input]

Much better!


Handling Other Errors

catch (\Throwable $e) {
    app_log(get_class($e) . ': ' . $e->getMessage());
    throw $e;
}

What is Throwable?

In PHP, \Throwable catches everything:

  • Exception (expected errors)
  • Error (fatal errors like syntax errors)

This is the broadest catch possible.

What Happens Here

  1. Log the error to storage/logs/app.log
  2. Re-throw it so developers see it

Why Log Then Re-throw?

Logging helps in production:

  • Errors are recorded even if users don't report them
  • You can debug issues after they happen

Re-throwing helps in development:

  • You see the full error message and stack trace
  • You can fix bugs immediately

The Logging Behavior

The app_log() function always logs errors to storage/logs/app.log, regardless of whether you are in development (APP_DEBUG=true) or production (APP_DEBUG=false).

This is the standard, expected behavior because:

  • In dev, you see the error on screen and have a written record.
  • In production, errors are hidden from the user but still written to the log file so you can debug them later.

Cleaning Up Flash Data

Session::unflash();

This is the last line in the file. It runs after the response is sent.

What Does It Do?

Removes flash data from the session:

unset($_SESSION['_flash']);

Why at the End?

Flash data should exist for exactly one request:

  1. Request 1: Store flash data
  2. Request 2: Read flash data, then delete it
  3. Request 3: Flash data is gone

By calling unflash() at the end of every request, we ensure flash data never lives longer than one request.


The Complete Error Flow

Validation Error Flow

User submits form

Controller validates

Validation fails

throw ValidationException

Entry point catches it

Store errors in flash
Store old input in flash

Redirect back to form

Form shows errors
Form pre-fills old input

End of request

Clean up flash data

Other Error Flow

Something breaks

throw Exception/Error

Entry point catches it

Log to file

Re-throw

PHP shows error (dev)
or error page (production)

Key Takeaways

  1. Two error types - Validation (expected) vs others (unexpected)
  2. Flash data for forms - Errors and old input survive one redirect
  3. Logging for production - Track errors even when hidden from users
  4. Clean up after yourself - Flash data is removed at request end

What's Good Here

✅ Validation errors provide great UX (redirect back with errors)
✅ Flash data is automatically cleaned up
✅ Errors are logged for debugging
✅ Separation between validation and fatal errors
✅ Simple enough to understand and extend yourself

Design Note

Error handling is intentionally minimal for learning purposes. You can see exactly how validation errors flow through the system. For production apps, you'd add custom error pages and error reporting services (Sentry, Bugsnag, etc.), but those would hide the underlying mechanics that DALT is trying to teach.


Part 1 Complete!

You now understand the entry point:

  • How requests enter the application
  • How sessions are managed
  • How the framework bootstraps itself
  • How errors are caught and handled

This is the foundation. Everything else builds on this.

On this page