- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
12 KiB
Console Command Best Practices
Best practices für Console Command Parameter im Custom PHP Framework.
Grundprinzipien
1. Prefer Typed Parameters over ConsoleInput ✅
Typed Parameters sind der bevorzugte Weg für Console Commands.
❌ Avoid: Manual parsing mit ConsoleInput
#[ConsoleCommand(name: 'user:create')]
public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode
{
$email = $input->getArgument('email');
$age = (int) ($input->getArgument('age') ?? 18);
// Manuelle Validierung
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return ExitCode::FAILURE;
}
}
✅ Prefer: Typed parameters
#[ConsoleCommand(name: 'user:create')]
public function execute(
ConsoleOutputInterface $output,
Email $email, // Value Object validation
int $age = 18 // Automatic type conversion
): ExitCode {
// Email already validated, age already int
}
2. Use Nullable Bool for Optional Flags ✅
Flags sollten als ?bool Parameter definiert werden, nicht als reguläre Parameter.
✅ Nullable Bool Flags
#[ConsoleCommand(name: 'deploy')]
public function execute(
ConsoleOutputInterface $output,
string $environment,
?bool $dryRun = null, // --dry-run flag
?bool $force = null, // --force flag
?bool $skipTests = null // --skip-tests flag
): ExitCode {
if ($dryRun === true) {
$output->writeLine("DRY RUN MODE");
}
if ($force === true) {
$output->writeLine("Forcing deployment");
}
}
Usage:
# No flags
php console.php deploy production
# dryRun=null, force=null, skipTests=null
# With flags
php console.php deploy production --dry-run --force
# dryRun=true, force=true, skipTests=null
3. ConsoleOutput only when needed ✅
ConsoleOutputInterface nur verwenden, wenn tatsächlich Output benötigt wird.
✅ With Output
public function execute(ConsoleOutputInterface $output, string $path): ExitCode
{
$output->writeLine("Processing {$path}...");
// ... processing
$output->writeLine("Done!");
return ExitCode::SUCCESS;
}
✅ Without Output (Silent)
public function execute(string $path): ExitCode
{
// Silent processing - no output needed
file_put_contents('/tmp/result.txt', $path);
return ExitCode::SUCCESS;
}
Parameter Types Übersicht
Framework Parameters (Auto-Injected)
Diese werden automatisch vom Framework bereitgestellt:
ConsoleInput $input // Variable args, raw access
ConsoleOutputInterface $output // User feedback
Position: Framework-Parameter können an beliebiger Position stehen.
// ✅ All valid positions
public function execute(ConsoleOutputInterface $output, string $name): ExitCode { }
public function execute(string $name, ConsoleOutputInterface $output): ExitCode { }
public function execute(string $name, int $age, ConsoleOutputInterface $output): ExitCode { }
User Parameters (From Command Line)
Diese werden von Command-Line-Argumenten aufgelöst:
Primitive Types:
string $name // String argument
int $age // Integer with validation
float $amount // Float/decimal number
bool $flag // Boolean flag (not recommended, use ?bool)
Nullable Bool for Flags:
?bool $verbose = null // Optional flag: null=not provided, true=--verbose
?bool $force = null // Optional flag: null=not provided, true=--force
Value Objects:
Email $email // Automatic Email validation
Url $website // Automatic URL validation
// Any VO with fromString() or __construct(string)
Enums:
enum Environment: string {
case DEV = 'development';
case PROD = 'production';
}
public function execute(Environment $env): ExitCode
Default Values:
string $name = 'Guest' // Optional with default
int $maxResults = 10 // Optional with default
?bool $verbose = null // Optional flag
Best Practices
Pattern 1: Simple Command with Required Args
#[ConsoleCommand(name: 'user:greet')]
public function execute(ConsoleOutputInterface $output, string $name): ExitCode
{
$output->writeLine("Hello, {$name}!");
return ExitCode::SUCCESS;
}
Usage: php console.php user:greet Alice
Pattern 2: Command with Optional Params
#[ConsoleCommand(name: 'user:list')]
public function execute(
ConsoleOutputInterface $output,
int $limit = 10,
int $offset = 0
): ExitCode {
$output->writeLine("Showing {$limit} users starting at {$offset}");
return ExitCode::SUCCESS;
}
Usage:
php console.php user:list(limit=10, offset=0)php console.php user:list 20(limit=20, offset=0)php console.php user:list 20 5(limit=20, offset=5)
Pattern 3: Command with Flags
#[ConsoleCommand(name: 'deploy')]
public function execute(
ConsoleOutputInterface $output,
string $environment,
?bool $dryRun = null,
?bool $force = null
): ExitCode {
$output->writeLine("Deploying to {$environment}");
if ($dryRun === true) {
$output->writeLine("DRY RUN MODE");
return ExitCode::SUCCESS;
}
if ($force === true) {
$output->writeLine("FORCING deployment");
}
// Actual deployment
return ExitCode::SUCCESS;
}
Usage:
php console.php deploy productionphp console.php deploy production --dry-runphp console.php deploy production --forcephp console.php deploy production --dry-run --force
Pattern 4: Value Objects for Validation
#[ConsoleCommand(name: 'send-email')]
public function execute(
ConsoleOutputInterface $output,
Email $to,
string $subject
): ExitCode {
// Email is already validated!
$output->writeLine("Sending email to {$to->value}");
$output->writeLine("Subject: {$subject}");
return ExitCode::SUCCESS;
}
Usage: php console.php send-email user@example.com "Welcome"
Pattern 5: Enums for Type Safety
enum LogLevel: string {
case DEBUG = 'debug';
case INFO = 'info';
case WARNING = 'warning';
case ERROR = 'error';
}
#[ConsoleCommand(name: 'log:show')]
public function execute(
ConsoleOutputInterface $output,
LogLevel $level = LogLevel::INFO
): ExitCode {
$output->writeLine("Showing {$level->value} logs");
return ExitCode::SUCCESS;
}
Usage:
php console.php log:show(level=info, default)php console.php log:show errorphp console.php log:show debug
Pattern 6: Variable Arguments (Special Case)
Only use ConsoleInput when you need:
- Variable number of arguments
- Raw command-line access
- Dynamic argument handling
#[ConsoleCommand(name: 'file:process')]
public function execute(
ConsoleInput $input,
ConsoleOutputInterface $output
): ExitCode {
$files = $input->getArguments();
if (empty($files)) {
$output->error('No files provided');
return ExitCode::FAILURE;
}
foreach ($files as $file) {
$output->writeLine("Processing {$file}");
}
return ExitCode::SUCCESS;
}
Usage: php console.php file:process file1.txt file2.txt file3.txt ...
When to Use What
Use Typed Parameters (90% of commands)
- ✅ Fixed number of arguments
- ✅ Known parameter types
- ✅ Type validation needed
- ✅ Value Objects or Enums
- ✅ Optional parameters with defaults
Use ConsoleInput (10% of commands)
- ✅ Variable number of arguments
- ✅ Many optional flags (>5)
- ✅ Raw command-line access needed
- ✅ Dynamic argument handling
- ✅ Argument count unknown at design time
Use ConsoleOutput
- ✅ Always when user feedback needed
- ✅ Progress indication
- ✅ Error messages
- ✅ Status updates
- ❌ Not needed for silent commands
Use Nullable Bool Flags
- ✅ Always for optional flags
- ✅ Three-state logic (null/false/true)
- ✅ Clean syntax:
--verboseinstead of--verbose=true - ✅ Multiple independent flags
Migration Guide
Old Style → New Style
Before (Old Style):
#[ConsoleCommand(name: 'user:create')]
public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode
{
$email = $input->getArgument('email') ?? throw new \Exception('Email required');
$age = (int) ($input->getArgument('age') ?? 18);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$output->error('Invalid email');
return ExitCode::FAILURE;
}
$output->writeLine("Creating user: {$email}, age: {$age}");
return ExitCode::SUCCESS;
}
After (New Style):
#[ConsoleCommand(name: 'user:create')]
public function execute(
ConsoleOutputInterface $output,
Email $email,
int $age = 18
): ExitCode {
// Email already validated, age already int
$output->writeLine("Creating user: {$email->value}, age: {$age}");
return ExitCode::SUCCESS;
}
Benefits:
- ✅ Type safety
- ✅ Automatic validation
- ✅ Less boilerplate
- ✅ Better IDE support
- ✅ Self-documenting
Adding Flags
Before:
public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode
{
$dryRun = $input->hasOption('dry-run');
$force = $input->hasOption('force');
if ($dryRun) {
$output->writeLine("DRY RUN");
}
}
After:
public function execute(
ConsoleOutputInterface $output,
?bool $dryRun = null,
?bool $force = null
): ExitCode {
if ($dryRun === true) {
$output->writeLine("DRY RUN");
}
}
Testing Commands
Testing with Typed Parameters
it('creates user with typed parameters', function () {
$method = new ReflectionMethod(UserCommand::class, 'execute');
$resolver = new CommandParameterResolver(new MethodSignatureAnalyzer());
$output = new ConsoleOutput();
// Test with required args
$params = $resolver->resolveParameters(
$method,
['alice@example.com', '25'], // email, age
null,
$output
);
expect($params[0])->toBeInstanceOf(ConsoleOutputInterface::class);
expect($params[1])->toBeInstanceOf(Email::class);
expect($params[2])->toBe(25);
});
Testing Flags
it('handles nullable bool flags correctly', function () {
$method = new ReflectionMethod(DeployCommand::class, 'execute');
// Without flags
$params = $resolver->resolveParameters(
$method,
['production'], // environment only
null,
$output
);
expect($params[2])->toBeNull(); // dryRun
expect($params[3])->toBeNull(); // force
// With flags
$params = $resolver->resolveParameters(
$method,
['production', '--dry-run', '--force'],
null,
$output
);
expect($params[2])->toBeTrue(); // dryRun
expect($params[3])->toBeTrue(); // force
});
Examples
Siehe src/Framework/Console/Examples/ConsoleParameterBestPracticesCommand.php für vollständige Beispiele aller Patterns.
Run examples:
docker exec php php console.php best:typed username
docker exec php php console.php best:flags production --verbose --force
docker exec php php console.php best:mixed Alice 25 --verified --active
docker exec php php console.php best:complex production --dry-run --skip-tests
Summary
| Pattern | Use Case | Example |
|---|---|---|
| Typed Parameters | Standard (90%) | string $name, int $age = 18 |
| Nullable Bool Flags | Optional flags | ?bool $verbose = null |
| Value Objects | Validation | Email $email, Url $website |
| Enums | Fixed choices | Environment $env |
| ConsoleInput | Variable args (10%) | ConsoleInput $input |
| ConsoleOutput | User feedback | ConsoleOutputInterface $output |
Remember:
- ✅ Typed parameters = type safety + validation
- ✅ Nullable bool = clean flag syntax
- ✅ Value Objects = automatic validation
- ✅ ConsoleInput only for special cases
- ✅ Framework parameters are optional and flexible