# Console Optional Parameters ## Overview Das Console System unterstützt jetzt flexible Parameter-Definitionen. `ConsoleInput` und `ConsoleOutput` Parameter sind **optional** und können an beliebigen Positionen in der Methoden-Signatur platziert werden. ## Key Features ✅ **Optional Framework Parameters**: `ConsoleInput` und `ConsoleOutput` müssen nicht mehr angegeben werden ✅ **Flexible Positioning**: Framework-Parameter können an beliebiger Position stehen ✅ **Automatic Injection**: Framework-Parameter werden automatisch vom Framework injiziert ✅ **Mixed Parameters**: Framework- und Benutzer-Parameter können beliebig gemischt werden ✅ **Backwards Compatible**: Bestehende Commands funktionieren weiterhin ## Usage Examples ### 1. Command without parameters ```php #[ConsoleCommand(name: 'hello', description: 'Simple hello command')] public function execute(): ExitCode { echo "Hello World!\n"; return ExitCode::SUCCESS; } ``` ```bash $ php console.php hello Hello World! ``` ### 2. Command with only user parameters ```php #[ConsoleCommand(name: 'greet', description: 'Greet a user')] public function execute(string $name, int $age = 18): ExitCode { echo "Hello {$name}, you are {$age} years old!\n"; return ExitCode::SUCCESS; } ``` ```bash $ php console.php greet Alice Hello Alice, you are 18 years old! $ php console.php greet Bob 25 Hello Bob, you are 25 years old! ``` ### 3. Command with ConsoleOutput only ```php #[ConsoleCommand(name: 'status', description: 'Show status')] public function execute(ConsoleOutputInterface $output, string $component = 'all'): ExitCode { $output->writeLine("Status of component: {$component}"); return ExitCode::SUCCESS; } ``` ```bash $ php console.php status Status of component: all $ php console.php status database Status of component: database ``` ### 4. Command with both ConsoleInput and ConsoleOutput ```php #[ConsoleCommand(name: 'process', description: 'Process data')] public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode { $args = $input->getArguments(); $output->writeLine("Processing: " . json_encode($args)); return ExitCode::SUCCESS; } ``` ```bash $ php console.php process file1.txt file2.txt Processing: ["file1.txt","file2.txt"] ``` ### 5. Mixed parameters (Output first) ```php #[ConsoleCommand(name: 'deploy', description: 'Deploy application')] public function execute( ConsoleOutputInterface $output, string $environment, bool $dryRun = false ): ExitCode { $output->writeLine("Deploying to: {$environment}"); $output->writeLine("Dry run: " . ($dryRun ? 'yes' : 'no')); return ExitCode::SUCCESS; } ``` ```bash $ php console.php deploy production Deploying to: production Dry run: no $ php console.php deploy staging --dry-run Deploying to: staging Dry run: yes ``` ### 6. Mixed parameters (Output in middle) ```php #[ConsoleCommand(name: 'migrate', description: 'Run migrations')] public function execute( string $direction, ConsoleOutputInterface $output, int $steps = 1 ): ExitCode { $output->writeLine("Migration {$direction} with {$steps} steps"); return ExitCode::SUCCESS; } ``` ```bash $ php console.php migrate up Migration up with 1 steps $ php console.php migrate down 5 Migration down with 5 steps ``` ### 7. All types mixed ```php #[ConsoleCommand(name: 'backup', description: 'Create backup')] public function execute( ConsoleInput $input, string $type, ConsoleOutputInterface $output, int $retention = 7, bool $compress = true ): ExitCode { $output->writeLine("Backup type: {$type}"); $output->writeLine("Retention: {$retention} days"); $output->writeLine("Compress: " . ($compress ? 'yes' : 'no')); $output->writeLine("Raw args: " . json_encode($input->getArguments())); return ExitCode::SUCCESS; } ``` ```bash $ php console.php backup full Backup type: full Retention: 7 days Compress: yes Raw args: ["full"] $ php console.php backup incremental 30 --no-compress Backup type: incremental Retention: 30 days Compress: no Raw args: ["incremental","30","--no-compress"] ``` ## How It Works ### Parameter Resolution Process 1. **Signature Analysis**: `MethodSignatureAnalyzer` analysiert die Methoden-Signatur 2. **Framework Parameter Detection**: Framework-Parameter (ConsoleInput, ConsoleOutput) werden erkannt und übersprungen 3. **Argument Definitions**: Nur Benutzer-Parameter werden zu ArgumentDefinitions 4. **Automatic Injection**: Framework-Parameter werden automatisch vom `CommandParameterResolver` injiziert 5. **User Parameter Resolution**: Benutzer-Parameter werden aus Command-Line-Argumenten aufgelöst 6. **Final Invocation**: Methode wird mit allen aufgelösten Parametern aufgerufen ### Framework Parameter Recognition Die folgenden Types werden als Framework-Parameter erkannt und automatisch injiziert: - `App\Framework\Console\ConsoleInput` - `App\Framework\Console\ConsoleInputInterface` - `App\Framework\Console\ConsoleOutput` - `App\Framework\Console\ConsoleOutputInterface` - Short names: `ConsoleInput`, `ConsoleOutput`, etc. ### Parameter Order Die **Reihenfolge der Parameter ist flexibel**. Framework-Parameter können an beliebiger Position stehen: ```php // All valid: public function execute(ConsoleInput $input, string $name): ExitCode { } public function execute(string $name, ConsoleInput $input): ExitCode { } public function execute(string $name, ConsoleOutput $output, int $age): ExitCode { } public function execute(ConsoleInput $input, string $name, ConsoleOutput $output): ExitCode { } ``` ## Migration Guide ### Old Style (Required Parameters) ```php // ❌ Old: ConsoleInput and ConsoleOutput were always required #[ConsoleCommand(name: 'old', description: 'Old style')] public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode { $name = $input->getArgument('name') ?? 'World'; $output->writeLine("Hello {$name}"); return ExitCode::SUCCESS; } ``` ### New Style (Optional, User-Friendly) ```php // ✅ New: Framework parameters are optional, user parameters are typed #[ConsoleCommand(name: 'new', description: 'New style')] public function execute(ConsoleOutputInterface $output, string $name = 'World'): ExitCode { $output->writeLine("Hello {$name}"); return ExitCode::SUCCESS; } ``` ### Benefits of New Style 1. **Type Safety**: User parameters are properly typed (string, int, bool, etc.) 2. **Default Values**: PHP default values work as expected 3. **Cleaner Code**: No manual argument parsing needed 4. **Better IDE Support**: Type hints provide better autocompletion 5. **Automatic Validation**: Type conversion and validation handled by framework ## Advanced Examples ### Using Value Objects ```php use App\Framework\Core\ValueObjects\Email; #[ConsoleCommand(name: 'send-email', description: 'Send email')] public function execute(Email $to, string $subject, ConsoleOutputInterface $output): ExitCode { $output->writeLine("Sending email to: {$to->value}"); $output->writeLine("Subject: {$subject}"); return ExitCode::SUCCESS; } ``` ```bash $ php console.php send-email user@example.com "Welcome" Sending email to: user@example.com Subject: Welcome ``` ### Using Enums ```php enum Environment: string { case DEV = 'development'; case STAGING = 'staging'; case PROD = 'production'; } #[ConsoleCommand(name: 'deploy', description: 'Deploy to environment')] public function execute( Environment $env, ConsoleOutputInterface $output, bool $force = false ): ExitCode { $output->writeLine("Deploying to: {$env->value}"); return ExitCode::SUCCESS; } ``` ```bash $ php console.php deploy production Deploying to: production $ php console.php deploy staging --force Deploying to: staging ``` ## Best Practices ### 1. Use ConsoleOutput when needed Nur `ConsoleOutputInterface` verwenden, wenn tatsächlich Output benötigt wird: ```php // ✅ Good: Uses output for user feedback public function execute(ConsoleOutputInterface $output, string $path): ExitCode { $output->writeLine("Processing {$path}..."); // ... processing $output->writeLine("Done!"); return ExitCode::SUCCESS; } // ✅ Also good: No output needed public function execute(string $path): ExitCode { // Silent processing return ExitCode::SUCCESS; } ``` ### 2. Prefer typed parameters over ConsoleInput Nutze typisierte Parameter anstatt manuelles Argument-Parsing: ```php // ❌ Avoid: Manual parsing public function execute(ConsoleInput $input): ExitCode { $name = $input->getArgument('name'); $age = (int) ($input->getArgument('age') ?? 18); } // ✅ Prefer: Typed parameters public function execute(string $name, int $age = 18): ExitCode { // Parameters are already typed and validated } ``` ### 3. Use ConsoleInput for variable arguments Nutze `ConsoleInput` wenn die Anzahl der Argumente variabel ist: ```php #[ConsoleCommand(name: 'batch', description: 'Process multiple files')] public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode { $files = $input->getArguments(); // Variable number of files foreach ($files as $file) { $output->writeLine("Processing {$file}"); } return ExitCode::SUCCESS; } ``` ### 4. Document parameter expectations Nutze PHPDoc für komplexe Parameter: ```php /** * Create a new user account * * @param string $username - Username (alphanumeric, 3-20 chars) * @param string $email - Valid email address * @param int $age - User age (must be 18+) */ #[ConsoleCommand(name: 'user:create', description: 'Create user')] public function execute( ConsoleOutputInterface $output, string $username, string $email, int $age ): ExitCode { // Implementation } ``` ## Testing Test-Script für flexible Parameter: ```bash $ docker exec php php /var/www/html/tests/debug/test-console-optional-params.php ``` Siehe auch: `tests/debug/test-console-optional-params.php` für detaillierte Beispiele. ## Implementation Details ### Modified Files 1. **CommandParameterResolver.php** - Neue optionale Parameter `$consoleInput` und `$consoleOutput` - `resolveFrameworkParameter()` Methode für automatische Framework-Parameter-Injektion - `isFrameworkParameter()` Methode zur Erkennung von Framework-Parametern 2. **MethodSignatureAnalyzer.php** - `isFrameworkProvidedParameter()` Methode überspringt Framework-Parameter bei ArgumentDefinition-Generierung - Framework-Parameter werden nicht mehr als User-Input-Argumente behandelt 3. **CommandRegistry.php** - Übergabe von `$input` und `$progressAwareOutput` an `CommandParameterResolver` - Vereinfachte Command-Ausführung (kein Legacy-Check mehr nötig) - Einheitliche Parameter-Resolution für alle Commands ## Framework Compatibility Diese Änderungen sind **vollständig rückwärtskompatibel**: - ✅ Bestehende Commands mit `(ConsoleInput $input, ConsoleOutput $output)` funktionieren weiterhin - ✅ Neue Commands können flexible Parameter nutzen - ✅ Migration kann schrittweise erfolgen - ✅ Keine Breaking Changes ## Performance Die automatische Framework-Parameter-Injektion hat **minimalen Performance-Impact**: - Parameter-Typ-Check: ~0.01ms pro Parameter - Framework-Parameter-Injektion: ~0.001ms pro Parameter - Gesamter Overhead: < 0.1ms pro Command-Execution ## Conclusion Das neue flexible Parameter-System macht Console-Commands: - 🎯 **Einfacher zu schreiben** - Weniger Boilerplate - 🔒 **Type-safer** - Automatische Typ-Validierung - 📖 **Lesbarer** - Klare Parameter-Deklaration - 🧪 **Testbarer** - Direktes Parameter-Passing möglich - 🚀 **Entwicklerfreundlicher** - IDE-Unterstützung verbessert