Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
29 / 29 |
|
96.88% |
31 / 32 |
|
66.67% |
14 / 21 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
| Commands | |
100.00% |
29 / 29 |
|
96.88% |
31 / 32 |
|
66.67% |
14 / 21 |
|
100.00% |
5 / 5 |
25.48 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| add | |
100.00% |
15 / 15 |
|
100.00% |
12 / 12 |
|
71.43% |
5 / 7 |
|
100.00% |
1 / 1 |
6.84 | |||
| entries | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| addArray | |
100.00% |
9 / 9 |
|
100.00% |
12 / 12 |
|
62.50% |
5 / 8 |
|
100.00% |
1 / 1 |
7.90 | |||
| validClass | |
100.00% |
3 / 3 |
|
83.33% |
5 / 6 |
|
50.00% |
2 / 4 |
|
100.00% |
1 / 1 |
2.50 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celema\Console; |
| 6 | |
| 7 | use Closure; |
| 8 | use ValueError; |
| 9 | |
| 10 | /** |
| 11 | * Collects command registrations. |
| 12 | * |
| 13 | * Accepts command instances, class-strings of zero-argument constructible |
| 14 | * commands, and lazy factories keyed by class-string: |
| 15 | * |
| 16 | * $commands = new Commands([ |
| 17 | * new Greet($translator), |
| 18 | * Simple::class, |
| 19 | * Expensive::class => fn() => new Expensive($db), |
| 20 | * ]); |
| 21 | * |
| 22 | * Commands carry their metadata in a #[Command] attribute. For a |
| 23 | * lightweight one-off, register an anonymous class — attributes work |
| 24 | * inline: |
| 25 | * |
| 26 | * $commands->add(new #[Command('cache:clear', 'Clears the cache')] class { |
| 27 | * public function __invoke(Io $io): int { ... } |
| 28 | * }); |
| 29 | * |
| 30 | * @api |
| 31 | */ |
| 32 | final class Commands |
| 33 | { |
| 34 | /** @var list<Entry> */ |
| 35 | private array $entries = []; |
| 36 | |
| 37 | public function __construct(array|object|string $commands = []) |
| 38 | { |
| 39 | $this->add($commands); |
| 40 | } |
| 41 | |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 45 | throw new ValueError( |
| 46 | 'Closure commands are not supported; use an anonymous class with a #[Command] attribute', |
| 47 | ); |
| 48 | } |
| 49 | |
| 50 | if ($commands instanceof Commands) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 52 | $this->entries[] = $entry; |
| 53 | } |
| 54 | |
| 55 | return; |
| 56 | } |
| 57 | |
| 58 | if (is_array($commands)) { |
| 59 | $this->addArray($commands); |
| 60 | |
| 61 | return; |
| 62 | } |
| 63 | |
| 64 | if (is_string($commands)) { |
| 65 | $this->entries[] = Entry::fromClass($this->validClass($commands)); |
| 66 | |
| 67 | return; |
| 68 | } |
| 69 | |
| 70 | $this->entries[] = Entry::fromInstance($commands); |
| 71 | } |
| 72 | |
| 73 | /** @return list<Entry> */ |
| 74 | public function entries(): array |
| 75 | { |
| 76 | return $this->entries; |
| 77 | } |
| 78 | |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /** @return class-string */ |
| 101 | private function validClass(string $class): string |
| 102 | { |
| 103 | if (!class_exists($class)) { |
| 104 | throw new ValueError("Unknown command class '{$class}'"); |
| 105 | } |
| 106 | |
| 107 | return $class; |
| 108 | } |
| 109 | } |
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.
| 37 | public function __construct(array|object|string $commands = []) |
| 38 | { |
| 39 | $this->add($commands); |
| 40 | } |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 45 | throw new ValueError( |
| 46 | 'Closure commands are not supported; use an anonymous class with a #[Command] attribute', |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 50 | if ($commands instanceof Commands) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 52 | $this->entries[] = $entry; |
| 51 | foreach ($commands->entries() as $entry) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 52 | $this->entries[] = $entry; |
| 53 | } |
| 54 | |
| 55 | return; |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 50 | if ($commands instanceof Commands) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 52 | $this->entries[] = $entry; |
| 53 | } |
| 54 | |
| 55 | return; |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 50 | if ($commands instanceof Commands) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 51 | foreach ($commands->entries() as $entry) { |
| 52 | $this->entries[] = $entry; |
| 53 | } |
| 54 | |
| 55 | return; |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 50 | if ($commands instanceof Commands) { |
| 58 | if (is_array($commands)) { |
| 59 | $this->addArray($commands); |
| 60 | |
| 61 | return; |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 50 | if ($commands instanceof Commands) { |
| 58 | if (is_array($commands)) { |
| 64 | if (is_string($commands)) { |
| 65 | $this->entries[] = Entry::fromClass($this->validClass($commands)); |
| 66 | |
| 67 | return; |
| 42 | public function add(array|object|string $commands): void |
| 43 | { |
| 44 | if ($commands instanceof Closure) { |
| 50 | if ($commands instanceof Commands) { |
| 58 | if (is_array($commands)) { |
| 64 | if (is_string($commands)) { |
| 70 | $this->entries[] = Entry::fromInstance($commands); |
| 71 | } |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 97 | } |
| 98 | } |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 97 | } |
| 98 | } |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 97 | } |
| 98 | } |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 97 | } |
| 98 | } |
| 79 | private function addArray(array $commands): void |
| 80 | { |
| 81 | foreach ($commands as $key => $item) { |
| 81 | foreach ($commands as $key => $item) { |
| 82 | if (is_string($key)) { |
| 83 | if (!$item instanceof Closure) { |
| 84 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 85 | } |
| 86 | |
| 87 | $this->entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 88 | |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if (!is_object($item) && !is_string($item)) { |
| 93 | throw new ValueError('Invalid command registration'); |
| 94 | } |
| 95 | |
| 96 | $this->add($item); |
| 97 | } |
| 98 | } |
| 76 | return $this->entries; |
| 77 | } |
| 101 | private function validClass(string $class): string |
| 102 | { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 104 | throw new ValueError("Unknown command class '{$class}'"); |
| 101 | private function validClass(string $class): string |
| 102 | { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 107 | return $class; |
| 108 | } |
| 101 | private function validClass(string $class): string |
| 102 | { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 104 | throw new ValueError("Unknown command class '{$class}'"); |
| 101 | private function validClass(string $class): string |
| 102 | { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 103 | if (!class_exists($class)) { |
| 107 | return $class; |
| 108 | } |