🎯 Probably the Only One!

The only PHP framework with native, built-from-scratch interactive error debugging in the console!

While other frameworks like Laravel rely on external tools like PsySH for interactive console debugging, Larafony implements its own sophisticated debug session system. When an exception occurs in your console commands, you don't just get a stack trace and exitβ€”you get an interactive REPL-like experience where you can explore frames, inspect variables, view source code, and understand the error context without leaving your terminal or installing external dependencies.

This is the same beautiful, web-like debugging experience you get in browsers... but available in your console! 😎

Console Error Debugging

Interactive, REPL-like debugging experience when exceptions occur in CLI commands

Native Implementation Interactive Session Frame Inspection Variable Explorer Source Viewer

What Makes This Unique?

πŸš€ Native, Built-in Implementation

Unlike other frameworks that rely on external packages like PsySH, Larafony's console debugger is built from scratch and deeply integrated into the framework. Zero external dependencies, zero configuration, just pure framework magic!

Key Features

How It Works

Automatic Registration

The ErrorHandlerServiceProvider automatically detects if your application is running in CLI mode and registers the appropriate handler. No configuration needed!

// In ErrorHandlerServiceProvider::register()
$isConsole = php_sapi_name() === 'cli';

if ($isConsole) {
    // Register console error handler with factory
    $factory = new ConsoleRendererFactory($container);
    $renderer = $factory->create();

    $handler = new ConsoleHandler(
        $renderer,
        fn(int $exitCode) => exit($exitCode)
    );

    $container->set(BaseHandler::class, $handler);
} else {
    // Register web error handler
    $handler = new DetailedErrorHandler($viewManager, $debug);
    $container->set(BaseHandler::class, $handler);
}

Service Provider Order

⚠️ Important: ConsoleServiceProvider must be registered before ErrorHandlerServiceProvider because it provides the OutputContract dependency.
// In bootstrap/console.php
$app->withServiceProviders([
    HttpServiceProvider::class,
    ConfigServiceProvider::class,
    ConsoleServiceProvider::class,    // ← Must come first
    DatabaseServiceProvider::class,
    ErrorHandlerServiceProvider::class, // ← Depends on ConsoleServiceProvider
]);

Interactive Debug Commands

When an exception occurs, you'll see a prompt where you can type commands:

Command Description
help Show all available commands
trace Display full stack trace
frame N Inspect frame number N in detail
vars Show variables in current frame
source Display source code around error
env Show environment information (PHP version, memory, etc.)
exit Exit debug session

Example Debug Session

Here's what you see when an exception occurs in a console command:

$ php bin/larafony process:data

Processing data...

[ERROR] RuntimeException
Data processing failed: Invalid format

in /var/www/app/Commands/ProcessDataCommand.php:24

Debug mode: (type 'help' for available commands) > help

Available commands:
  trace    - Show full stack trace
  frame N  - Show details for frame N
  vars     - Show variables in current frame
  source   - Show source code around error
  env      - Show environment information
  exit     - Exit debug mode

Debug mode: (type 'help' for available commands) > trace

Stack trace:
  #0 ProcessDataCommand->processComplexData()
     at /var/www/app/Commands/ProcessDataCommand.php:24

  #1 ProcessDataCommand->handle()
     at /var/www/app/Commands/ProcessDataCommand.php:15

  #2 Kernel->handleCommand()
     at /var/www/framework/src/Console/Kernel.php:42

Debug mode: (type 'help' for available commands) > frame 0

Frame #0: ProcessDataCommand->processComplexData()
File: /var/www/app/Commands/ProcessDataCommand.php:24

Source code:
  20 β”‚
  21 β”‚     private function processComplexData(): array
  22 β”‚     {
  23 β”‚         // This will trigger the console error handler
> 24 β”‚         throw new \RuntimeException('Data processing failed: Invalid format');
  25 β”‚     }
  26 β”‚ }

Variables:
  $this = ProcessDataCommand {#42}
  $output = Output {#12}

Debug mode: (type 'help' for available commands) > env

Environment Information:
  PHP Version: 8.5.0
  Memory Usage: 4.2 MB / 512 MB
  Execution Time: 0.15s
  Operating System: Linux
  SAPI: cli

Debug mode: (type 'help' for available commands) > exit

Architecture

Core Components

Component Purpose
BaseHandler Abstract base for all error handlers (web + console)
ConsoleHandler CLI-specific error handler
ConsoleRenderer Orchestrates rendering of exception details
DebugSession Interactive command loop (REPL)
ConsoleRendererFactory Constructs renderer with all dependencies

BaseHandler Abstract Class

Provides common error handling functionality:

abstract class BaseHandler
{
    // Register PHP error/exception handlers
    public function register(): void
    {
        set_error_handler([$this, 'handleError']);
        set_exception_handler([$this, 'handleException']);
        register_shutdown_function([$this, 'handleFatalError']);
    }

    // Convert PHP errors to exceptions
    public function handleError(int $level, string $message, ...): bool
    {
        throw new ErrorException($message, 0, $level, $file, $line);
    }

    // Catch fatal errors during shutdown
    public function handleFatalError(): void
    {
        $error = error_get_last();
        if ($error !== null && $this->isFatalError($error['type'])) {
            $this->handleException(new FatalError(...));
        }
    }

    abstract public function handleException(Throwable $exception): void;
}

Fatal Error Types

The handler catches these fatal errors:

Comparison with Other Solutions

Feature Larafony Laravel (Tinker + PsySH) Symfony 7.4
Interactive Debugging Built-in External (PsySH) Not Available
Dependencies Zero Requires psy/psysh symfony/error-handler
Installation Automatic composer require laravel/tinker Automatic
Frame Navigation
Variable Inspection
Source Code View
Clean Terminal Output (Since 7.4)
Trigger On Exception Manual (tinker command) On Exception
πŸ’‘ Key Difference:

Larafony's interactive debugger activates automatically when exceptions occur, without requiring external tools or manual invocation. Laravel's Tinker is primarily a REPL for exploring your application, not an automatic exception handler. Symfony 7.4 improved console error display but doesn't offer interactive debugging.

Creating a Test Command

Try it yourself by creating a simple command that throws an exception:

<?php

namespace App\Commands;

use Larafony\Framework\Console\Attributes\Command;
use Larafony\Framework\Console\Contracts\CommandContract;
use Larafony\Framework\Console\Contracts\OutputContract;

#[Command(name: 'test:error', description: 'Test console error handler')]
class TestErrorCommand implements CommandContract
{
    public function __construct(private OutputContract $output)
    {
    }

    public function handle(array $options = []): int
    {
        $this->output->info('About to trigger an exception...');

        // This will activate the interactive debugger
        throw new \RuntimeException('Test exception for debugging!');
    }
}

Run it with:

php bin/larafony test:error

You'll immediately drop into the interactive debug session!

Best Practices

Future Enhancements

Planned improvements for the console debugger:

πŸŽ‰ Conclusion

Larafony's console debugger represents a significant innovation in PHP framework error handling. By building this functionality from scratch and integrating it deeply into the framework, we've created a debugging experience that's both powerful and developer-friendly. No external dependencies, no complex setupβ€”just pure, interactive debugging magic! 😎