Console Error Debugging

🎯 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! 😎

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:

  • E_ERROR - Fatal run-time errors

  • E_PARSE - Compile-time parse errors

  • E_CORE_ERROR - Fatal errors during PHP's initial startup

  • E_COMPILE_ERROR - Fatal compile-time errors

  • E_USER_ERROR - User-generated error message

  • E_USER_DEPRECATED - User-generated deprecation notice

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

  • Development Only - The interactive debugger is perfect for development. In production, exceptions should be logged

  • Explore Thoroughly - Use trace to see the full picture, then frame N to dive deep

  • Check Variables - Always inspect variables with vars to understand state

  • Exit Cleanly - Type exit to quit, or Ctrl+C for immediate termination

  • Use for Learning - Great way to understand framework internals and execution flow

Future Enhancements

Planned improvements for the console debugger:

  • Code execution in frame context (eval)

  • Search within stack traces

  • Export debug session to file

  • Custom command extensions

  • Integration with logging

πŸŽ‰ 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! 😎