# 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**
```php
#[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**
```php
#[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**
```php
#[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:**
```bash
# 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**
```php
public function execute(ConsoleOutputInterface $output, string $path): ExitCode
{
    $output->writeLine("Processing {$path}...");
    // ... processing
    $output->writeLine("Done!");
    return ExitCode::SUCCESS;
}
```

**✅ Without Output (Silent)**
```php
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:

```php
ConsoleInput $input              // Variable args, raw access
ConsoleOutputInterface $output   // User feedback
```

**Position**: Framework-Parameter können an beliebiger Position stehen.

```php
// ✅ 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:**
```php
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:**
```php
?bool $verbose = null  // Optional flag: null=not provided, true=--verbose
?bool $force = null    // Optional flag: null=not provided, true=--force
```

**Value Objects:**
```php
Email $email          // Automatic Email validation
Url $website          // Automatic URL validation
// Any VO with fromString() or __construct(string)
```

**Enums:**
```php
enum Environment: string {
    case DEV = 'development';
    case PROD = 'production';
}

public function execute(Environment $env): ExitCode
```

**Default Values:**
```php
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

```php
#[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

```php
#[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

```php
#[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 production`
- `php console.php deploy production --dry-run`
- `php console.php deploy production --force`
- `php console.php deploy production --dry-run --force`

### Pattern 4: Value Objects for Validation

```php
#[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

```php
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 error`
- `php 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

```php
#[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: `--verbose` instead of `--verbose=true`
- ✅ Multiple independent flags

## Migration Guide

### Old Style → New Style

**Before (Old Style):**
```php
#[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):**
```php
#[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:**
```php
public function execute(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode
{
    $dryRun = $input->hasOption('dry-run');
    $force = $input->hasOption('force');

    if ($dryRun) {
        $output->writeLine("DRY RUN");
    }
}
```

**After:**
```php
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

```php
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

```php
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:**
```bash
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
