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

codeigniter4 / CodeIgniter4 / 29271483819

13 Jul 2026 05:42PM UTC coverage: 88.71% (+0.006%) from 88.704%
29271483819

Pull #10374

github

web-flow
Merge 3e59f033c into 8c164d05c
Pull Request #10374: fix: resolve race conditions in Redis TTL and prevent cache test state leakage

22322 of 25163 relevant lines covered (88.71%)

213.82 hits per line

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

94.85
/system/HTTP/IncomingRequest.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\Exceptions\InvalidArgumentException;
17
use CodeIgniter\HTTP\Exceptions\HTTPException;
18
use CodeIgniter\HTTP\Files\FileCollection;
19
use CodeIgniter\HTTP\Files\UploadedFile;
20
use Config\App;
21
use Config\Services;
22
use Locale;
23
use stdClass;
24

25
/**
26
 * Class IncomingRequest
27
 *
28
 * Represents an incoming, server-side HTTP request.
29
 *
30
 * Per the HTTP specification, this interface includes properties for
31
 * each of the following:
32
 *
33
 * - Protocol version
34
 * - HTTP method
35
 * - URI
36
 * - Headers
37
 * - Message body
38
 *
39
 * Additionally, it encapsulates all data as it has arrived to the
40
 * application from the CGI and/or PHP environment, including:
41
 *
42
 * - The values represented in $_SERVER.
43
 * - Any cookies provided (generally via $_COOKIE)
44
 * - Query string arguments (generally via $_GET, or as parsed via parse_str())
45
 * - Upload files, if any (as represented by $_FILES)
46
 * - Deserialized body binds (generally from $_POST)
47
 *
48
 * @see \CodeIgniter\HTTP\IncomingRequestTest
49
 */
50
class IncomingRequest extends Request
51
{
52
    /**
53
     * The URI for this request.
54
     *
55
     * Note: This WILL NOT match the actual URL in the browser since for
56
     * everything this cares about (and the router, etc) is the portion
57
     * AFTER the baseURL. So, if hosted in a sub-folder this will
58
     * appear different than actual URI path. If you need that use getPath().
59
     *
60
     * @var URI
61
     */
62
    protected $uri;
63

64
    /**
65
     * The detected URI path (relative to the baseURL).
66
     *
67
     * Note: current_url() uses this to build its URI,
68
     * so this becomes the source for the "current URL"
69
     * when working with the share request instance.
70
     *
71
     * @var string|null
72
     */
73
    protected $path;
74

75
    /**
76
     * File collection
77
     *
78
     * @var FileCollection|null
79
     */
80
    protected $files;
81

82
    /**
83
     * Negotiator
84
     *
85
     * @var Negotiate|null
86
     */
87
    protected $negotiator;
88

89
    /**
90
     * The default Locale this request
91
     * should operate under.
92
     *
93
     * @var string
94
     */
95
    protected $defaultLocale;
96

97
    /**
98
     * The current locale of the application.
99
     * Default value is set in app/Config/App.php
100
     *
101
     * @var string
102
     */
103
    protected $locale;
104

105
    /**
106
     * Stores the valid locale codes.
107
     *
108
     * @var array
109
     */
110
    protected $validLocales = [];
111

112
    /**
113
     * Holds the old data from a redirect.
114
     *
115
     * @var array
116
     */
117
    protected $oldInput = [];
118

119
    /**
120
     * The user agent this request is from.
121
     *
122
     * @var UserAgent
123
     */
124
    protected $userAgent;
125

126
    /**
127
     * Constructor
128
     *
129
     * @param App         $config
130
     * @param string|null $body
131
     */
132
    public function __construct($config, ?URI $uri = null, $body = 'php://input', ?UserAgent $userAgent = null)
133
    {
134
        if (! $uri instanceof URI || ! $userAgent instanceof UserAgent) {
1,634✔
135
            throw new InvalidArgumentException('You must supply the parameters: uri, userAgent.');
×
136
        }
137

138
        $this->populateHeaders();
1,634✔
139

140
        if (
141
            $body === 'php://input'
1,634✔
142
            // php://input is not available with enctype="multipart/form-data".
143
            // See https://www.php.net/manual/en/wrappers.php.php#wrappers.php.input
144
            && ! str_contains($this->getHeaderLine('Content-Type'), 'multipart/form-data')
1,634✔
145
            && (int) $this->getHeaderLine('Content-Length') <= $this->getPostMaxSize()
1,634✔
146
        ) {
147
            // Get our body from php://input
148
            $body = file_get_contents('php://input');
1,193✔
149
        }
150

151
        // If file_get_contents() returns false or empty string, set null.
152
        if ($body === false || $body === '') {
1,634✔
153
            $body = null;
1,197✔
154
        }
155

156
        $this->uri          = $uri;
1,634✔
157
        $this->body         = $body;
1,634✔
158
        $this->userAgent    = $userAgent;
1,634✔
159
        $this->validLocales = $config->supportedLocales;
1,634✔
160

161
        parent::__construct($config);
1,634✔
162

163
        if ($uri instanceof SiteURI) {
1,634✔
164
            $this->setPath($uri->getRoutePath());
1,634✔
165
        } else {
166
            $this->setPath($uri->getPath());
×
167
        }
168

169
        $this->detectLocale($config);
1,634✔
170
    }
171

172
    private function getPostMaxSize(): int
173
    {
174
        $postMaxSize = ini_get('post_max_size');
1,193✔
175

176
        return match (strtoupper(substr($postMaxSize, -1))) {
1,193✔
177
            'G'     => (int) str_replace('G', '', $postMaxSize) * 1024 ** 3,
×
178
            'M'     => (int) str_replace('M', '', $postMaxSize) * 1024 ** 2,
1,193✔
179
            'K'     => (int) str_replace('K', '', $postMaxSize) * 1024,
×
180
            default => (int) $postMaxSize,
1,193✔
181
        };
1,193✔
182
    }
183

184
    /**
185
     * Handles setting up the locale, perhaps auto-detecting through
186
     * content negotiation.
187
     *
188
     * @param App $config
189
     *
190
     * @return void
191
     */
192
    public function detectLocale($config)
193
    {
194
        $this->locale = $this->defaultLocale = $config->defaultLocale;
1,634✔
195

196
        if (! $config->negotiateLocale) {
1,634✔
197
            return;
1,634✔
198
        }
199

200
        $this->setLocale($this->negotiate('language', $config->supportedLocales));
2✔
201
    }
202

203
    /**
204
     * Provides a convenient way to work with the Negotiate class
205
     * for content negotiation.
206
     */
207
    public function negotiate(string $type, array $supported, bool $strictMatch = false): string
208
    {
209
        if ($this->negotiator === null) {
8✔
210
            $this->negotiator = Services::negotiator($this, true);
8✔
211
        }
212

213
        return match (strtolower($type)) {
8✔
214
            'media'    => $this->negotiator->media($supported, $strictMatch),
2✔
215
            'charset'  => $this->negotiator->charset($supported),
1✔
216
            'encoding' => $this->negotiator->encoding($supported),
1✔
217
            'language' => $this->negotiator->language($supported),
3✔
218
            default    => throw HTTPException::forInvalidNegotiationType($type),
8✔
219
        };
8✔
220
    }
221

222
    /**
223
     * Checks this request type.
224
     */
225
    public function is(string $type): bool
226
    {
227
        $valueUpper = strtoupper($type);
47✔
228

229
        $httpMethods = Method::all();
47✔
230

231
        if (in_array($valueUpper, $httpMethods, true)) {
47✔
232
            return $this->getMethod() === $valueUpper;
44✔
233
        }
234

235
        if ($valueUpper === 'JSON') {
3✔
236
            return str_contains($this->getHeaderLine('Content-Type'), 'application/json');
1✔
237
        }
238

239
        if ($valueUpper === 'AJAX') {
2✔
240
            return $this->isAJAX();
1✔
241
        }
242

243
        throw new InvalidArgumentException('Unknown type: ' . $type);
1✔
244
    }
245

246
    /**
247
     * Determines if this request was made from the command line (CLI).
248
     */
249
    public function isCLI(): bool
250
    {
251
        return false;
1✔
252
    }
253

254
    /**
255
     * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
256
     */
257
    public function isAJAX(): bool
258
    {
259
        return $this->hasHeader('X-Requested-With')
81✔
260
            && strtolower($this->header('X-Requested-With')->getValue()) === 'xmlhttprequest';
81✔
261
    }
262

263
    /**
264
     * Attempts to detect if the current connection is secure through
265
     * a few different methods.
266
     */
267
    public function isSecure(): bool
268
    {
269
        $https = service('superglobals')->server('HTTPS');
21✔
270

271
        if ($https !== null && strtolower($https) !== 'off') {
21✔
272
            return true;
1✔
273
        }
274

275
        if (! $this->isFromTrustedProxy()) {
20✔
276
            return false;
14✔
277
        }
278

279
        if ($this->hasHeader('X-Forwarded-Proto') && $this->header('X-Forwarded-Proto')->getValue() === 'https') {
6✔
280
            return true;
3✔
281
        }
282

283
        return $this->hasHeader('Front-End-Https') && ! empty($this->header('Front-End-Https')->getValue()) && strtolower($this->header('Front-End-Https')->getValue()) !== 'off';
3✔
284
    }
285

286
    /**
287
     * Sets the URI path relative to baseURL.
288
     *
289
     * Note: Since current_url() accesses the shared request
290
     * instance, this can be used to change the "current URL"
291
     * for testing.
292
     *
293
     * @param string $path URI path relative to baseURL
294
     *
295
     * @return $this
296
     */
297
    private function setPath(string $path)
298
    {
299
        $this->path = $path;
1,634✔
300

301
        return $this;
1,634✔
302
    }
303

304
    /**
305
     * Returns the URI path relative to baseURL,
306
     * running detection as necessary.
307
     */
308
    public function getPath(): string
309
    {
310
        return $this->path;
99✔
311
    }
312

313
    /**
314
     * Sets the locale string for this request.
315
     *
316
     * @return IncomingRequest
317
     */
318
    public function setLocale(string $locale)
319
    {
320
        // If it's not a valid locale, set it
321
        // to the default locale for the site.
322
        if (! in_array($locale, $this->validLocales, true)) {
5✔
323
            $locale = $this->defaultLocale;
1✔
324
        }
325

326
        $this->locale = $locale;
5✔
327
        Locale::setDefault($locale);
5✔
328

329
        return $this;
5✔
330
    }
331

332
    /**
333
     * Set the valid locales.
334
     *
335
     * @return $this
336
     */
337
    public function setValidLocales(array $locales)
338
    {
339
        $this->validLocales = $locales;
1✔
340

341
        return $this;
1✔
342
    }
343

344
    /**
345
     * Gets the current locale, with a fallback to the default
346
     * locale if none is set.
347
     */
348
    public function getLocale(): string
349
    {
350
        return $this->locale;
537✔
351
    }
352

353
    /**
354
     * Returns the default locale as set in app/Config/App.php
355
     */
356
    public function getDefaultLocale(): string
357
    {
358
        return $this->defaultLocale;
3✔
359
    }
360

361
    /**
362
     * Fetch an item from JSON input stream with fallback to $_REQUEST object. This is the simplest way
363
     * to grab data from the request object and can be used in lieu of the
364
     * other get* methods in most cases.
365
     *
366
     * @param array|string|null $index
367
     * @param int|null          $filter Filter constant
368
     * @param array|int|null    $flags
369
     *
370
     * @return array<array-key, mixed>|bool|float|int|stdClass|string|null
371
     */
372
    public function getVar($index = null, $filter = null, $flags = null)
373
    {
374
        if (
375
            str_contains($this->getHeaderLine('Content-Type'), 'application/json')
18✔
376
            && $this->body !== null
18✔
377
        ) {
378
            return $this->getJsonVar($index, false, $filter, $flags);
1✔
379
        }
380

381
        return $this->fetchGlobal('request', $index, $filter, $flags);
17✔
382
    }
383

384
    /**
385
     * A convenience method that grabs the raw input stream and decodes
386
     * the JSON into an array.
387
     *
388
     * If $assoc == true, then all objects in the response will be converted
389
     * to associative arrays.
390
     *
391
     * @param bool $assoc   Whether to return objects as associative arrays
392
     * @param int  $depth   How many levels deep to decode
393
     * @param int  $options Bitmask of options
394
     *
395
     * @see http://php.net/manual/en/function.json-decode.php
396
     *
397
     * @return array<array-key, mixed>|bool|float|int|stdClass|null
398
     *
399
     * @throws HTTPException When the body is invalid as JSON.
400
     */
401
    public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0)
402
    {
403
        if ($this->body === null) {
21✔
404
            return null;
2✔
405
        }
406

407
        $result = json_decode($this->body, $assoc, $depth, $options);
19✔
408

409
        if (json_last_error() !== JSON_ERROR_NONE) {
18✔
410
            throw HTTPException::forInvalidJSON(json_last_error_msg());
3✔
411
        }
412

413
        return $result;
15✔
414
    }
415

416
    /**
417
     * Get a specific variable from a JSON input stream
418
     *
419
     * @param array|string|null $index  The variable that you want which can use dot syntax for getting specific values.
420
     * @param bool              $assoc  If true, return the result as an associative array.
421
     * @param int|null          $filter Filter Constant
422
     * @param array|int|null    $flags  Option
423
     *
424
     * @return array<array-key, mixed>|bool|float|int|stdClass|string|null
425
     */
426
    public function getJsonVar($index = null, bool $assoc = false, ?int $filter = null, $flags = null)
427
    {
428
        helper('array');
6✔
429

430
        $data = $this->getJSON(true);
6✔
431
        if (! is_array($data)) {
6✔
432
            return null;
1✔
433
        }
434

435
        if (is_string($index)) {
5✔
436
            $data = dot_array_search($index, $data);
5✔
437
        } elseif (is_array($index)) {
2✔
438
            $result = [];
2✔
439

440
            foreach ($index as $key) {
2✔
441
                $result[$key] = dot_array_search($key, $data);
2✔
442
            }
443

444
            [$data, $result] = [$result, null];
2✔
445
        }
446

447
        if ($data === null) {
5✔
448
            return null;
2✔
449
        }
450

451
        $filter ??= FILTER_UNSAFE_RAW;
5✔
452
        $flags = is_array($flags) ? $flags : (is_numeric($flags) ? (int) $flags : 0);
5✔
453

454
        if ($filter !== FILTER_UNSAFE_RAW
5✔
455
            || (
456
                (is_numeric($flags) && $flags !== 0)
5✔
457
                || is_array($flags) && $flags !== []
5✔
458
            )
459
        ) {
460
            if (is_array($data)) {
2✔
461
                // Iterate over array and append filter and flags
462
                array_walk_recursive($data, static function (&$val) use ($filter, $flags): void {
1✔
463
                    $valType = gettype($val);
1✔
464
                    $val     = filter_var($val, $filter, $flags);
1✔
465

466
                    if (in_array($valType, ['int', 'integer', 'float', 'double', 'bool', 'boolean'], true) && $val !== false) {
1✔
467
                        settype($val, $valType);
1✔
468
                    }
469
                });
1✔
470
            } else {
471
                $dataType = gettype($data);
1✔
472
                $data     = filter_var($data, $filter, $flags);
1✔
473

474
                if (in_array($dataType, ['int', 'integer', 'float', 'double', 'bool', 'boolean'], true) && $data !== false) {
1✔
475
                    settype($data, $dataType);
×
476
                }
477
            }
478
        }
479

480
        if (! $assoc) {
5✔
481
            if (is_array($index)) {
4✔
482
                foreach ($data as &$val) {
2✔
483
                    $val = is_array($val) ? json_decode(json_encode($val)) : $val;
2✔
484
                }
485

486
                return $data;
2✔
487
            }
488

489
            return json_decode(json_encode($data));
3✔
490
        }
491

492
        return $data;
2✔
493
    }
494

495
    /**
496
     * A convenience method that grabs the raw input stream(send method in PUT, PATCH, DELETE) and decodes
497
     * the String into an array.
498
     *
499
     * @return array
500
     */
501
    public function getRawInput()
502
    {
503
        parse_str($this->body ?? '', $output);
17✔
504

505
        return $output;
17✔
506
    }
507

508
    /**
509
     * Gets a specific variable from raw input stream (send method in PUT, PATCH, DELETE).
510
     *
511
     * @param array|string|null $index  The variable that you want which can use dot syntax for getting specific values.
512
     * @param int|null          $filter Filter Constant
513
     * @param array|int|null    $flags  Option
514
     *
515
     * @return mixed
516
     */
517
    public function getRawInputVar($index = null, ?int $filter = null, $flags = null)
518
    {
519
        helper('array');
10✔
520

521
        parse_str($this->body ?? '', $output);
10✔
522

523
        if (is_string($index)) {
10✔
524
            $output = dot_array_search($index, $output);
6✔
525
        } elseif (is_array($index)) {
4✔
526
            $data = [];
2✔
527

528
            foreach ($index as $key) {
2✔
529
                $data[$key] = dot_array_search($key, $output);
2✔
530
            }
531

532
            [$output, $data] = [$data, null];
2✔
533
        }
534

535
        $filter ??= FILTER_UNSAFE_RAW;
10✔
536
        $flags = is_array($flags) ? $flags : (is_numeric($flags) ? (int) $flags : 0);
10✔
537

538
        if (is_array($output)
10✔
539
            && (
540
                $filter !== FILTER_UNSAFE_RAW
10✔
541
                || (
10✔
542
                    (is_numeric($flags) && $flags !== 0)
10✔
543
                    || is_array($flags) && $flags !== []
10✔
544
                )
10✔
545
            )
546
        ) {
547
            // Iterate over array and append filter and flags
548
            array_walk_recursive($output, static function (&$val) use ($filter, $flags): void {
×
549
                $val = filter_var($val, $filter, $flags);
×
550
            });
×
551

552
            return $output;
×
553
        }
554

555
        if (is_string($output)) {
10✔
556
            return filter_var($output, $filter, $flags);
5✔
557
        }
558

559
        return $output;
5✔
560
    }
561

562
    /**
563
     * Fetch an item from GET data.
564
     *
565
     * @param array|string|null $index  Index for item to fetch from $_GET.
566
     * @param int|null          $filter A filter name to apply.
567
     * @param array|int|null    $flags
568
     *
569
     * @return mixed
570
     */
571
    public function getGet($index = null, $filter = null, $flags = null)
572
    {
573
        return $this->fetchGlobal('get', $index, $filter, $flags);
82✔
574
    }
575

576
    /**
577
     * Fetch an item from POST.
578
     *
579
     * @param array|string|null $index  Index for item to fetch from $_POST.
580
     * @param int|null          $filter A filter name to apply
581
     * @param array|int|null    $flags
582
     *
583
     * @return mixed
584
     */
585
    public function getPost($index = null, $filter = null, $flags = null)
586
    {
587
        return $this->fetchGlobal('post', $index, $filter, $flags);
108✔
588
    }
589

590
    /**
591
     * Fetch an item from POST data with fallback to GET.
592
     *
593
     * @param array|string|null $index  Index for item to fetch from $_POST or $_GET
594
     * @param int|null          $filter A filter name to apply
595
     * @param array|int|null    $flags
596
     *
597
     * @return mixed
598
     */
599
    public function getPostGet($index = null, $filter = null, $flags = null)
600
    {
601
        if ($index === null) {
6✔
602
            return array_merge($this->getGet($index, $filter, $flags), $this->getPost($index, $filter, $flags));
3✔
603
        }
604

605
        if (is_array($index)) {
3✔
606
            $output = [];
1✔
607

608
            foreach ($index as $key) {
1✔
609
                $output[$key] = $this->getPostGet($key, $filter, $flags);
1✔
610
            }
611

612
            return $output;
1✔
613
        }
614

615
        // Use $_POST directly here, since filter_has_var only
616
        // checks the initial POST data, not anything that might
617
        // have been added since.
618
        return service('superglobals')->post($index) !== null
3✔
619
            ? $this->getPost($index, $filter, $flags)
2✔
620
            : (service('superglobals')->get($index) !== null ? $this->getGet($index, $filter, $flags) : $this->getPost($index, $filter, $flags));
3✔
621
    }
622

623
    /**
624
     * Fetch an item from GET data with fallback to POST.
625
     *
626
     * @param array|string|null $index  Index for item to be fetched from $_GET or $_POST
627
     * @param int|null          $filter A filter name to apply
628
     * @param array|int|null    $flags
629
     *
630
     * @return mixed
631
     */
632
    public function getGetPost($index = null, $filter = null, $flags = null)
633
    {
634
        if ($index === null) {
6✔
635
            return array_merge($this->getPost($index, $filter, $flags), $this->getGet($index, $filter, $flags));
3✔
636
        }
637

638
        if (is_array($index)) {
3✔
639
            $output = [];
1✔
640

641
            foreach ($index as $key) {
1✔
642
                $output[$key] = $this->getGetPost($key, $filter, $flags);
1✔
643
            }
644

645
            return $output;
1✔
646
        }
647

648
        // Use $_GET directly here, since filter_has_var only
649
        // checks the initial GET data, not anything that might
650
        // have been added since.
651
        return service('superglobals')->get($index) !== null
3✔
652
            ? $this->getGet($index, $filter, $flags)
2✔
653
            : (service('superglobals')->post($index) !== null ? $this->getPost($index, $filter, $flags) : $this->getGet($index, $filter, $flags));
3✔
654
    }
655

656
    /**
657
     * Fetch an item from the COOKIE array.
658
     *
659
     * @param array|string|null $index  Index for item to be fetched from $_COOKIE
660
     * @param int|null          $filter A filter name to be applied
661
     * @param array|int|null    $flags
662
     *
663
     * @return mixed
664
     */
665
    public function getCookie($index = null, $filter = null, $flags = null)
666
    {
667
        return $this->fetchGlobal('cookie', $index, $filter, $flags);
136✔
668
    }
669

670
    /**
671
     * Fetch the user agent string
672
     *
673
     * @return UserAgent
674
     */
675
    public function getUserAgent()
676
    {
677
        return $this->userAgent;
1✔
678
    }
679

680
    /**
681
     * Attempts to get old Input data that has been flashed to the session
682
     * with redirect_with_input(). It first checks for the data in the old
683
     * POST data, then the old GET data and finally check for dot arrays
684
     *
685
     * @return array|string|null
686
     */
687
    public function getOldInput(string $key)
688
    {
689
        // If the session hasn't been started, we're done.
690
        if (! isset($_SESSION)) {
20✔
691
            return null;
×
692
        }
693

694
        // Get previously saved in session
695
        $old = session('_ci_old_input');
20✔
696

697
        // If no data was previously saved, we're done.
698
        if ($old === null) {
20✔
699
            return null;
6✔
700
        }
701

702
        // Check for the value in the POST array first.
703
        if (isset($old['post'][$key])) {
16✔
704
            return $old['post'][$key];
13✔
705
        }
706

707
        // Next check in the GET array.
708
        if (isset($old['get'][$key])) {
8✔
709
            return $old['get'][$key];
3✔
710
        }
711

712
        helper('array');
6✔
713

714
        // Check for an array value in POST.
715
        if (isset($old['post'])) {
6✔
716
            $value = dot_array_search($key, $old['post']);
6✔
717
            if ($value !== null) {
6✔
718
                return $value;
1✔
719
            }
720
        }
721

722
        // Check for an array value in GET.
723
        if (isset($old['get'])) {
6✔
724
            $value = dot_array_search($key, $old['get']);
3✔
725
            if ($value !== null) {
3✔
726
                return $value;
1✔
727
            }
728
        }
729

730
        // requested session key not found
731
        return null;
5✔
732
    }
733

734
    /**
735
     * Returns an array of all files that have been uploaded with this
736
     * request. Each file is represented by an UploadedFile instance.
737
     */
738
    public function getFiles(): array
739
    {
740
        if ($this->files === null) {
1✔
741
            $this->files = new FileCollection();
1✔
742
        }
743

744
        return $this->files->all(); // return all files
1✔
745
    }
746

747
    /**
748
     * Verify if a file exist, by the name of the input field used to upload it, in the collection
749
     * of uploaded files and if is have been uploaded with multiple option.
750
     *
751
     * @return array|null
752
     */
753
    public function getFileMultiple(string $fileID)
754
    {
755
        if ($this->files === null) {
79✔
756
            $this->files = new FileCollection();
79✔
757
        }
758

759
        return $this->files->getFileMultiple($fileID);
79✔
760
    }
761

762
    /**
763
     * Retrieves a single file by the name of the input field used
764
     * to upload it.
765
     *
766
     * @return UploadedFile|null
767
     */
768
    public function getFile(string $fileID)
769
    {
770
        if ($this->files === null) {
75✔
771
            $this->files = new FileCollection();
1✔
772
        }
773

774
        return $this->files->getFile($fileID);
75✔
775
    }
776
}
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