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
-
Interactive Debug Session - Drop into a REPL-like interface when exceptions occur
-
Command-Based Exploration - Type commands to explore stack traces, frames, variables, and source code
-
Automatic Environment Detection - Detects CLI vs web mode via php_sapi_name()
-
Beautiful Console Output - Color-coded, syntax-highlighted display
-
Frame Navigation - Jump between stack frames to understand execution flow
-
Variable Inspection - View all variables available in each frame
-
Source Code Display - See code snippets with context around errors
-
Fatal Error Recovery - Catches shutdown errors (E_ERROR, E_PARSE, E_CORE_ERROR, etc.)
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! π
| | |