Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
93.48% covered (success)
93.48%
43 / 46
25.00% covered (danger)
25.00%
14 / 56
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Opts
100.00% covered (success)
100.00%
43 / 43
93.48% covered (success)
93.48%
43 / 46
25.00% covered (danger)
25.00%
14 / 56
100.00% covered (success)
100.00%
6 / 6
207.05
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 has
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 all
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 getOpts
100.00% covered (success)
100.00%
19 / 19
86.96% covered (warning)
86.96%
20 / 23
0.00% covered (danger)
0.00%
0 / 42
100.00% covered (success)
100.00%
1 / 1
7
 validate
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Celemas\Cli;
6
7use ValueError;
8
9/**
10 * PHP's native `getopt` stops after the first "non-option" argument
11 * which in our case is the command in `php run <command>`.
12 *
13 * `-arg`, `--arg` and even `---arg` are recognized but treated as different flags.
14 *
15 * @api
16 */
17final class Opts
18{
19    private readonly array $opts;
20
21    public function __construct()
22    {
23        $this->opts = $this->getOpts();
24    }
25
26    public function has(string $key, bool $default = false): bool
27    {
28        if (array_key_exists($key, $this->opts)) {
29            return true;
30        }
31
32        return $default;
33    }
34
35    public function get(string $key, string $default = ''): string
36    {
37        if (func_num_args() === 1) {
38            $this->validate($key);
39
40            return $this->opts[$key]->get();
41        }
42
43        if (!$this->has($key)) {
44            return $default;
45        }
46
47        if ($this->opts[$key]->isset()) {
48            return $this->opts[$key]->get();
49        }
50
51        return $default;
52    }
53
54    public function all(string $key, array $default = []): array
55    {
56        if (func_num_args() === 1) {
57            $this->validate($key);
58
59            return $this->opts[$key]->all();
60        }
61
62        if (!$this->has($key)) {
63            return $default;
64        }
65
66        if ($this->opts[$key]->isset()) {
67            return $this->opts[$key]->all();
68        }
69
70        return $default;
71    }
72
73    private static function getOpts(): array
74    {
75        $opts = [];
76        $key = null;
77
78        foreach ($_SERVER['argv'] ?? [] as $arg) {
79            if (!str_starts_with($arg, '-')) {
80                if ($key !== null) {
81                    $opts[$key]->set($arg);
82                }
83
84                continue;
85            }
86
87            $key = $arg;
88            $value = null;
89
90            if (str_contains($key, '=')) {
91                $parts = explode('=', $key);
92                $key = array_shift($parts);
93                $value = implode('=', $parts);
94            }
95
96            if (!array_key_exists($key, $opts)) {
97                $opts[$key] = new Opt($value);
98
99                continue;
100            }
101
102            if ($value !== null) {
103                $opts[$key]->set($value);
104            }
105        }
106
107        return $opts;
108    }
109
110    private function validate(string $key): void
111    {
112        if (!array_key_exists($key, $this->opts)) {
113            throw new ValueError("Unknown option: {$key}");
114        }
115
116        if (!$this->opts[$key]->isset()) {
117            throw new ValueError("No value given for {$key}");
118        }
119    }
120}