Writing Console Commands
This guide covers how to author your own console commands in Glueful, built on Symfony Console with dependency-injection integration.
Overview
Glueful's console system provides a powerful command-line interface for application management, built on Symfony Console with dependency injection integration and enhanced features for modern development workflows.
Key Features
- Symfony Console Integration: Modern CLI framework with advanced features
- Dependency Injection: Full DI container integration for all commands
- Enhanced Styling: SymfonyStyle integration with custom styling helpers
- Interactive Commands: Rich user interaction with confirmations, choices, and progress bars
- Production Safety: Built-in production environment safeguards
- Extensible Architecture: Easy custom command creation with base classes
- Multi-format Output: Support for table, JSON, and compact output formats
Console Architecture
The console system is built around several key components:
- Application: Central console application managing all commands
- BaseCommand: Enhanced base class with DI integration and styling
- Command Registry: Automatic command discovery and registration
- Service Integration: Full access to application services and utilities
Console Architecture
Application Class
use Glueful\Console\Application;
$container = container();
$app = new Application($container);
$app->run();
Key Features:
- Automatic command registration via DI container
- Enhanced error handling and exception management
- Consistent branding and help system
- Command categorization and organization
BaseCommand Class
All Glueful commands extend the enhanced BaseCommand:
use Glueful\Console\BaseCommand;
class MyCommand extends BaseCommand
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->info('Starting operation...');
$this->success('Operation completed!');
return self::SUCCESS;
}
}
Enhanced Methods:
Output Methods:
$this->success($message)- Green success message with icon$this->error($message)- Red error message with icon$this->warning($message)- Yellow warning message with icon$this->info($message)- Blue info message$this->note($message)- Highlighted note/tip$this->tip($message)- Legacy tip method (adds "Tip:" prefix)$this->line($message)- Plain text line
Interactive Methods:
$this->confirm($question, $default)- Yes/no confirmation$this->ask($question, $default)- Text input with optional default$this->secret($question)- Hidden input for passwords$this->choice($question, $choices, $default)- Multiple choice selection
Display Methods:
$this->table($headers, $rows)- Formatted table display$this->createProgressBar($max)- Create progress bar instance$this->progressBar($steps, $callback)- Progress bar with callback
Utility Methods:
$this->getService($serviceId)- Resolve service from DI container$this->getServiceDynamic($serviceId)- Dynamic service resolution$this->getContainer()- Access DI container$this->isProduction()- Check if running in production$this->confirmProduction($operation)- Force production confirmation
Custom Commands
Creating Custom Commands
<?php
namespace App\Console\Commands;
use Glueful\Console\BaseCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(
name: 'my:command',
description: 'Custom command description'
)]
class MyCommand extends BaseCommand
{
protected function configure(): void
{
$this->setDescription('Custom command description')
->setHelp('Detailed help text for the command')
->addArgument(
'name',
InputArgument::REQUIRED,
'Name argument'
)
->addOption(
'force',
'f',
InputOption::VALUE_NONE,
'Force execution'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$name = $input->getArgument('name');
$force = $input->getOption('force');
// Production safety check
if (!$force && !$this->confirmProduction('execute custom operation')) {
return self::FAILURE;
}
try {
$this->info("Starting operation for: {$name}");
// Access services via DI container
$myService = $this->getService(MyService::class);
// Progress bar example
$this->progressBar(10, function ($progressBar) use ($myService) {
for ($i = 0; $i < 10; $i++) {
$myService->doSomething();
$progressBar->advance();
sleep(1);
}
});
$this->success('Operation completed successfully!');
return self::SUCCESS;
} catch (\Exception $e) {
$this->error('Operation failed: ' . $e->getMessage());
return self::FAILURE;
}
}
}
Registering Custom Commands
// In your service provider or bootstrap file
$app = new Glueful\Console\Application($container);
$app->addCommand(MyCommand::class);
Command Templates
Use a concrete scaffold command instead of a generic generate:command helper:
# Create domain-specific classes with the scaffold commands that exist today
php glueful scaffold:job SendDigestEmail
php glueful scaffold:middleware EnsureAdmin
php glueful scaffold:test UserControllerTest
Best Practices
Command Design
- Use Descriptive Names: Follow the
group:actionpattern - Provide Good Help: Include detailed descriptions and examples
- Handle Errors Gracefully: Use try-catch blocks and meaningful error messages
- Production Safety: Always check production environment for destructive operations
- Progress Feedback: Use progress bars for long-running operations
Input Validation
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Validate required options
$email = $input->getOption('email');
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error('Invalid email address provided');
return self::FAILURE;
}
// Validate file paths
$file = $input->getArgument('file');
if (!file_exists($file)) {
$this->error("File not found: {$file}");
return self::FAILURE;
}
return self::SUCCESS;
}
Interactive Commands
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Confirmation prompts
if (!$this->confirm('Do you want to continue?', false)) {
$this->info('Operation cancelled');
return self::SUCCESS;
}
// Choice menus
$action = $this->choice(
'What would you like to do?',
['create', 'update', 'delete'],
'create'
);
// Text input with validation
$name = $this->ask('Enter name');
while (empty($name)) {
$this->warning('Name cannot be empty');
$name = $this->ask('Enter name');
}
return self::SUCCESS;
}
Output Formatting
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Table display
$this->table(
['ID', 'Name', 'Status'],
[
[1, 'John Doe', 'Active'],
[2, 'Jane Smith', 'Inactive']
]
);
// Progress tracking
$items = range(1, 100);
$this->progressBar(count($items), function ($progressBar) use ($items) {
foreach ($items as $item) {
// Process item
sleep(0.1);
$progressBar->advance();
}
});
return self::SUCCESS;
}
Error Handling
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// Risky operation
$result = $this->performOperation();
if (!$result) {
$this->warning('Operation completed with warnings');
return self::SUCCESS;
}
$this->success('Operation completed successfully');
return self::SUCCESS;
} catch (ValidationException $e) {
$this->error('Validation failed: ' . $e->getMessage());
return self::INVALID;
} catch (\Exception $e) {
$this->error('Unexpected error: ' . $e->getMessage());
if ($input->getOption('verbose')) {
$this->line($e->getTraceAsString());
}
return self::FAILURE;
}
}
Service Integration
class MyCommand extends BaseCommand
{
private MyService $myService;
private DatabaseInterface $database;
public function __construct()
{
parent::__construct();
// Resolve services from DI container
$this->myService = $this->getService(MyService::class);
$this->database = $this->getService(DatabaseInterface::class);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Use services
$users = $this->database->select('users')->get();
$result = $this->myService->processUsers($users);
return self::SUCCESS;
}
}
Configuration and Environment
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Check environment
if ($this->isProduction() && !$input->getOption('force')) {
if (!$this->confirmProduction('perform this operation')) {
return self::FAILURE;
}
}
// Access configuration
$timeout = config($this->getContext(), 'app.timeout', 30);
$debug = config($this->getContext(), 'app.debug', false);
return self::SUCCESS;
}
This comprehensive console system provides powerful tools for application management, development workflows, and system administration while maintaining consistency, safety, and ease of use.
Memory Management
Glueful's memory-management and observability tooling — the memory manager, alerting, efficient iterators, object pool, chunked DB processing, lazy container, and request tracing.
Glueful Extensions
Learn how to create, configure, and optimize Glueful extensions using the ServiceProvider pattern, Composer discovery, and production caching.