Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
28 / 28 |
|
100.00% |
25 / 25 |
|
68.42% |
13 / 19 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
| Option | |
100.00% |
28 / 28 |
|
100.00% |
25 / 25 |
|
68.42% |
13 / 19 |
|
100.00% |
3 / 3 |
18.32 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
8 / 8 |
|
100.00% |
8 / 8 |
|
37.50% |
3 / 8 |
|
100.00% |
1 / 1 |
7.91 | |||
| label | |
100.00% |
1 / 1 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| value | |
100.00% |
19 / 19 |
|
100.00% |
13 / 13 |
|
88.89% |
8 / 9 |
|
100.00% |
1 / 1 |
7.07 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celema\Console; |
| 6 | |
| 7 | use BackedEnum; |
| 8 | use Celema\Console\Exception\InvalidUsage; |
| 9 | use ReflectionParameter; |
| 10 | |
| 11 | /** |
| 12 | * An `#[Opt]` parameter of a command's `__invoke()` or of an option group's |
| 13 | * constructor. |
| 14 | * |
| 15 | * @internal |
| 16 | */ |
| 17 | final class Option |
| 18 | { |
| 19 | public readonly string $name; |
| 20 | |
| 21 | /** |
| 22 | * The `--no-` name that sets a flag to false, if its default is |
| 23 | * `true` or `null`. |
| 24 | */ |
| 25 | public readonly ?string $negation; |
| 26 | |
| 27 | /** |
| 28 | * @param ?string $group The `__invoke()` parameter of the option group |
| 29 | * declaring the option, if any |
| 30 | */ |
| 31 | public function __construct( |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * The `<value>` label of a value-taking option. |
| 49 | */ |
| 50 | public function label(): string |
| 51 | { |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Reads the value of the option, which occurs in the input. |
| 57 | * |
| 58 | * @param string $given The name in the input: the option's own or its |
| 59 | * negation |
| 60 | * @param int $occurrences How often the option occurs, bare or with a |
| 61 | * value; Args merges the occurrences |
| 62 | * @param bool $bare Whether one of them has no value |
| 63 | * @return bool|list<string>|string|int|float|BackedEnum |
| 64 | */ |
| 65 | public function value( |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 74 | if ($values !== []) { |
| 75 | throw new InvalidUsage("Option '{$given}' does not accept a value"); |
| 76 | } |
| 77 | |
| 78 | return $given !== $this->negation; |
| 79 | } |
| 80 | |
| 81 | // Every occurrence needs a value, also when a repetition provides |
| 82 | // one: `--host --host=x` must not hide the bare `--host`. |
| 83 | if ($bare) { |
| 84 | if ($this->opt->bare === null) { |
| 85 | throw new InvalidUsage("Option '{$this->name}' requires a value: {$this->name}=<{$this->label()}>"); |
| 86 | } |
| 87 | |
| 88 | $values[] = $this->opt->bare; |
| 89 | } |
| 90 | |
| 91 | if ($this->type->name === 'array') { |
| 92 | return $values; |
| 93 | } |
| 94 | |
| 95 | if ($occurrences !== 1) { |
| 96 | throw new InvalidUsage("Option '{$this->name}' accepts only one value"); |
| 97 | } |
| 98 | |
| 99 | return ( |
| 100 | $this->type->convert($values[0]) |
| 101 | ?? throw new InvalidUsage( |
| 102 | "Option '{$this->name}' expects {$this->type->expected()}, got '{$values[0]}'", |
| 103 | ) |
| 104 | ); |
| 105 | } |
| 106 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 42 | && $parameter->getDefaultValue() !== false |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 42 | && $parameter->getDefaultValue() !== false |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 32 | public readonly ReflectionParameter $parameter, |
| 33 | public readonly Opt $opt, |
| 34 | public readonly Type $type, |
| 35 | public readonly ?string $group = null, |
| 36 | ) { |
| 37 | $kebab = Signature::kebab($parameter->getName()); |
| 38 | $this->name = '--' . $kebab; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 39 | $this->negation = |
| 40 | $type->name === 'bool' |
| 41 | && $parameter->isDefaultValueAvailable() |
| 42 | && $parameter->getDefaultValue() !== false |
| 43 | ? '--no-' . $kebab |
| 44 | : null; |
| 45 | } |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 53 | } |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 52 | return $this->opt->value === '' ? substr($this->name, offset: 2) : $this->opt->value; |
| 53 | } |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 74 | if ($values !== []) { |
| 75 | throw new InvalidUsage("Option '{$given}' does not accept a value"); |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 74 | if ($values !== []) { |
| 78 | return $given !== $this->negation; |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 84 | if ($this->opt->bare === null) { |
| 85 | throw new InvalidUsage("Option '{$this->name}' requires a value: {$this->name}=<{$this->label()}>"); |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 84 | if ($this->opt->bare === null) { |
| 88 | $values[] = $this->opt->bare; |
| 89 | } |
| 90 | |
| 91 | if ($this->type->name === 'array') { |
| 91 | if ($this->type->name === 'array') { |
| 92 | return $values; |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 84 | if ($this->opt->bare === null) { |
| 88 | $values[] = $this->opt->bare; |
| 89 | } |
| 90 | |
| 91 | if ($this->type->name === 'array') { |
| 91 | if ($this->type->name === 'array') { |
| 95 | if ($occurrences !== 1) { |
| 96 | throw new InvalidUsage("Option '{$this->name}' accepts only one value"); |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 84 | if ($this->opt->bare === null) { |
| 88 | $values[] = $this->opt->bare; |
| 89 | } |
| 90 | |
| 91 | if ($this->type->name === 'array') { |
| 91 | if ($this->type->name === 'array') { |
| 95 | if ($occurrences !== 1) { |
| 100 | $this->type->convert($values[0]) |
| 101 | ?? throw new InvalidUsage( |
| 102 | "Option '{$this->name}' expects {$this->type->expected()}, got '{$values[0]}'", |
| 103 | ) |
| 104 | ); |
| 105 | } |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 91 | if ($this->type->name === 'array') { |
| 92 | return $values; |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 91 | if ($this->type->name === 'array') { |
| 95 | if ($occurrences !== 1) { |
| 96 | throw new InvalidUsage("Option '{$this->name}' accepts only one value"); |
| 66 | Args $args, |
| 67 | string $given, |
| 68 | int $occurrences, |
| 69 | bool $bare, |
| 70 | ): bool|array|string|int|float|BackedEnum { |
| 71 | $values = $args->opts($given); |
| 72 | |
| 73 | if ($this->type->name === 'bool') { |
| 83 | if ($bare) { |
| 91 | if ($this->type->name === 'array') { |
| 95 | if ($occurrences !== 1) { |
| 100 | $this->type->convert($values[0]) |
| 101 | ?? throw new InvalidUsage( |
| 102 | "Option '{$this->name}' expects {$this->type->expected()}, got '{$values[0]}'", |
| 103 | ) |
| 104 | ); |
| 105 | } |