• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 23980423122

04 Apr 2026 02:02PM UTC coverage: 86.568% (+0.009%) from 86.559%
23980423122

Pull #10079

github

web-flow
Merge d546783cf into 3e14cd13a
Pull Request #10079: feat: add support for parsing array options

45 of 46 new or added lines in 3 files covered. (97.83%)

13 existing lines in 1 file now uncovered.

22706 of 26229 relevant lines covered (86.57%)

220.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.24
/system/HTTP/CLIRequest.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\HTTP;
15

16
use CodeIgniter\CLI\CommandLineParser;
17
use CodeIgniter\Exceptions\RuntimeException;
18
use Config\App;
19
use Locale;
20

21
/**
22
 * Represents a request from the command-line. Provides additional
23
 * tools to interact with that request since CLI requests are not
24
 * static like HTTP requests might be.
25
 *
26
 * Portions of this code were initially from the FuelPHP Framework,
27
 * version 1.7.x, and used here under the MIT license they were
28
 * originally made available under.
29
 *
30
 * http://fuelphp.com
31
 *
32
 * @see \CodeIgniter\HTTP\CLIRequestTest
33
 */
34
class CLIRequest extends Request
35
{
36
    /**
37
     * Stores the segments of our cli "URI" command.
38
     *
39
     * @var list<string>
40
     */
41
    protected $segments = [];
42

43
    /**
44
     * Command line options and their values.
45
     *
46
     * @var array<string, list<string|null>|string|null>
47
     */
48
    protected $options = [];
49

50
    /**
51
     * Command line arguments (segments and options).
52
     *
53
     * @var array<int|string, list<string|null>|string|null>
54
     */
55
    protected $args = [];
56

57
    /**
58
     * Set the expected HTTP verb
59
     *
60
     * @var string
61
     */
62
    protected $method = 'CLI';
63

64
    /**
65
     * Constructor
66
     */
67
    public function __construct(App $config)
68
    {
69
        if (! is_cli()) {
80✔
70
            throw new RuntimeException(static::class . ' needs to run from the command line.'); // @codeCoverageIgnore
×
71
        }
72

73
        parent::__construct($config);
80✔
74

75
        // Don't terminate the script when the cli's tty goes away
76
        ignore_user_abort(true);
80✔
77

78
        $parser = new CommandLineParser($this->getServer('argv') ?? []);
80✔
79

80
        $this->segments = $parser->getArguments();
80✔
81
        $this->options  = $parser->getOptions();
80✔
82
        $this->args     = $parser->getTokens();
80✔
83

84
        // Set SiteURI for this request
85
        $this->uri = new SiteURI($config, $this->getPath());
80✔
86
    }
87

88
    /**
89
     * Returns the "path" of the request script so that it can be used
90
     * in routing to the appropriate controller/method.
91
     *
92
     * The path is determined by treating the command line arguments
93
     * as if it were a URL - up until we hit our first option.
94
     *
95
     * Example:
96
     *      php index.php users 21 profile -foo bar
97
     *
98
     *      // Routes to /users/21/profile (index is removed for routing sake)
99
     *      // with the option foo = bar.
100
     */
101
    public function getPath(): string
102
    {
103
        return implode('/', $this->segments);
80✔
104
    }
105

106
    /**
107
     * Returns an associative array of all CLI options found, with
108
     * their values.
109
     *
110
     * @return array<string, list<string|null>|string|null>
111
     */
112
    public function getOptions(): array
113
    {
114
        return $this->options;
1✔
115
    }
116

117
    /**
118
     * Returns an array of all CLI arguments (segments and options).
119
     *
120
     * @return array<int|string, list<string|null>|string|null>
121
     */
122
    public function getArgs(): array
123
    {
124
        return $this->args;
1✔
125
    }
126

127
    /**
128
     * Returns the path segments.
129
     *
130
     * @return list<string>
131
     */
132
    public function getSegments(): array
133
    {
134
        return $this->segments;
2✔
135
    }
136

137
    /**
138
     * Returns the value for a single CLI option that was passed in.
139
     *
140
     * @return list<string|null>|string|null
141
     */
142
    public function getOption(string $key)
143
    {
144
        return $this->options[$key] ?? null;
1✔
145
    }
146

147
    /**
148
     * Returns the options as a string, suitable for passing along on
149
     * the CLI to other commands.
150
     *
151
     * Example:
152
     *      $options = [
153
     *          'foo' => 'bar',
154
     *          'baz' => 'queue some stuff'
155
     *      ];
156
     *
157
     *      getOptionString() = '-foo bar -baz "queue some stuff"'
158
     */
159
    public function getOptionString(bool $useLongOpts = false): string
160
    {
161
        if ($this->options === []) {
8✔
162
            return '';
1✔
163
        }
164

165
        $out = [];
7✔
166

167
        $valueCallback = static function (?string $value, string $name) use (&$out): void {
7✔
168
            if ($value === null) {
7✔
169
                $out[] = $name;
1✔
170
            } elseif (str_contains($value, ' ')) {
7✔
171
                $out[] = sprintf('%s "%s"', $name, $value);
5✔
172
            } else {
173
                $out[] = sprintf('%s %s', $name, $value);
6✔
174
            }
175
        };
7✔
176

177
        foreach ($this->options as $name => $value) {
7✔
178
            $name = $useLongOpts && mb_strlen($name) > 1 ? "--{$name}" : "-{$name}";
7✔
179

180
            if (is_array($value)) {
7✔
181
                foreach ($value as $val) {
1✔
182
                    $valueCallback($val, $name);
1✔
183
                }
184
            } else {
185
                $valueCallback($value, $name);
7✔
186
            }
187
        }
188

189
        return trim(implode(' ', $out));
7✔
190
    }
191

192
    /**
193
     * Parses the command line it was called from and collects all options
194
     * and valid segments.
195
     *
196
     * NOTE: I tried to use getopt but had it fail occasionally to find
197
     * any options, where argv has always had our back.
198
     *
199
     * @deprecated 4.8.0 No longer used.
200
     *
201
     * @return void
202
     */
203
    protected function parseCommand()
204
    {
UNCOV
205
        @trigger_error(sprintf('The %s() method is deprecated and no longer used.', __METHOD__), E_USER_DEPRECATED);
×
206

207
        $args = $this->getServer('argv');
×
UNCOV
208
        array_shift($args); // Scrap index.php
×
209

UNCOV
210
        $optionValue = false;
×
211

UNCOV
212
        foreach ($args as $i => $arg) {
×
UNCOV
213
            if (mb_strpos($arg, '-') !== 0) {
×
UNCOV
214
                if ($optionValue) {
×
UNCOV
215
                    $optionValue = false;
×
216
                } else {
UNCOV
217
                    $this->segments[] = $arg;
×
UNCOV
218
                    $this->args[]     = $arg;
×
219
                }
220

UNCOV
221
                continue;
×
222
            }
223

UNCOV
224
            $arg   = ltrim($arg, '-');
×
225
            $value = null;
×
226

UNCOV
227
            if (isset($args[$i + 1]) && mb_strpos($args[$i + 1], '-') !== 0) {
×
228
                $value       = $args[$i + 1];
×
UNCOV
229
                $optionValue = true;
×
230
            }
231

232
            $this->options[$arg] = $value;
×
233
            $this->args[$arg]    = $value;
×
234
        }
235
    }
236

237
    /**
238
     * Determines if this request was made from the command line (CLI).
239
     */
240
    public function isCLI(): bool
241
    {
242
        return true;
1✔
243
    }
244

245
    /**
246
     * Fetch an item from GET data.
247
     *
248
     * @param array|string|null $index  Index for item to fetch from $_GET.
249
     * @param int|null          $filter A filter name to apply.
250
     * @param array|int|null    $flags
251
     *
252
     * @return array|null
253
     */
254
    public function getGet($index = null, $filter = null, $flags = null)
255
    {
256
        return $this->returnNullOrEmptyArray($index);
1✔
257
    }
258

259
    /**
260
     * Fetch an item from POST.
261
     *
262
     * @param array|string|null $index  Index for item to fetch from $_POST.
263
     * @param int|null          $filter A filter name to apply
264
     * @param array|int|null    $flags
265
     *
266
     * @return array|null
267
     */
268
    public function getPost($index = null, $filter = null, $flags = null)
269
    {
270
        return $this->returnNullOrEmptyArray($index);
1✔
271
    }
272

273
    /**
274
     * Fetch an item from POST data with fallback to GET.
275
     *
276
     * @param array|string|null $index  Index for item to fetch from $_POST or $_GET
277
     * @param int|null          $filter A filter name to apply
278
     * @param array|int|null    $flags
279
     *
280
     * @return array|null
281
     */
282
    public function getPostGet($index = null, $filter = null, $flags = null)
283
    {
284
        return $this->returnNullOrEmptyArray($index);
1✔
285
    }
286

287
    /**
288
     * Fetch an item from GET data with fallback to POST.
289
     *
290
     * @param array|string|null $index  Index for item to be fetched from $_GET or $_POST
291
     * @param int|null          $filter A filter name to apply
292
     * @param array|int|null    $flags
293
     *
294
     * @return array|null
295
     */
296
    public function getGetPost($index = null, $filter = null, $flags = null)
297
    {
298
        return $this->returnNullOrEmptyArray($index);
1✔
299
    }
300

301
    /**
302
     * This is a place holder for calls from cookie_helper get_cookie().
303
     *
304
     * @param array|string|null $index  Index for item to be fetched from $_COOKIE
305
     * @param int|null          $filter A filter name to be applied
306
     * @param mixed             $flags
307
     *
308
     * @return array|null
309
     */
310
    public function getCookie($index = null, $filter = null, $flags = null)
311
    {
312
        return $this->returnNullOrEmptyArray($index);
1✔
313
    }
314

315
    /**
316
     * @param array|string|null $index
317
     *
318
     * @return array|null
319
     */
320
    private function returnNullOrEmptyArray($index)
321
    {
322
        return ($index === null || is_array($index)) ? [] : null;
5✔
323
    }
324

325
    /**
326
     * Gets the current locale, with a fallback to the default
327
     * locale if none is set.
328
     */
329
    public function getLocale(): string
330
    {
331
        return Locale::getDefault();
1✔
332
    }
333

334
    /**
335
     * Checks this request type.
336
     */
337
    public function is(string $type): bool
338
    {
339
        return false;
1✔
340
    }
341
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc