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

codeigniter4 / CodeIgniter4 / 25481830444

07 May 2026 07:18AM UTC coverage: 88.332% (+0.008%) from 88.324%
25481830444

Pull #10158

github

web-flow
Merge af3068c6b into 3a1047073
Pull Request #10158: feat: add typed FormRequest accessors

87 of 96 new or added lines in 5 files covered. (90.63%)

15 existing lines in 2 files now uncovered.

23726 of 26860 relevant lines covered (88.33%)

218.0 hits per line

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

91.03
/system/Config/BaseService.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\Config;
15

16
use CodeIgniter\Autoloader\Autoloader;
17
use CodeIgniter\Autoloader\FileLocator;
18
use CodeIgniter\Autoloader\FileLocatorCached;
19
use CodeIgniter\Autoloader\FileLocatorInterface;
20
use CodeIgniter\Cache\CacheInterface;
21
use CodeIgniter\Cache\ResponseCache;
22
use CodeIgniter\CLI\Commands;
23
use CodeIgniter\CodeIgniter;
24
use CodeIgniter\Database\ConnectionInterface;
25
use CodeIgniter\Database\MigrationRunner;
26
use CodeIgniter\Debug\Exceptions;
27
use CodeIgniter\Debug\Iterator;
28
use CodeIgniter\Debug\Timer;
29
use CodeIgniter\Debug\Toolbar;
30
use CodeIgniter\Email\Email;
31
use CodeIgniter\Encryption\EncrypterInterface;
32
use CodeIgniter\EnvironmentDetector;
33
use CodeIgniter\Exceptions\InvalidArgumentException;
34
use CodeIgniter\Filters\Filters;
35
use CodeIgniter\Format\Format;
36
use CodeIgniter\Honeypot\Honeypot;
37
use CodeIgniter\HTTP\CLIRequest;
38
use CodeIgniter\HTTP\ContentSecurityPolicy;
39
use CodeIgniter\HTTP\CURLRequest;
40
use CodeIgniter\HTTP\IncomingRequest;
41
use CodeIgniter\HTTP\Negotiate;
42
use CodeIgniter\HTTP\RedirectResponse;
43
use CodeIgniter\HTTP\Request;
44
use CodeIgniter\HTTP\RequestInterface;
45
use CodeIgniter\HTTP\ResponseInterface;
46
use CodeIgniter\HTTP\SiteURIFactory;
47
use CodeIgniter\HTTP\URI;
48
use CodeIgniter\Images\Handlers\BaseHandler;
49
use CodeIgniter\Input\InputData;
50
use CodeIgniter\Language\Language;
51
use CodeIgniter\Lock\LockManager;
52
use CodeIgniter\Log\Logger;
53
use CodeIgniter\Pager\Pager;
54
use CodeIgniter\Router\RouteCollection;
55
use CodeIgniter\Router\RouteCollectionInterface;
56
use CodeIgniter\Router\Router;
57
use CodeIgniter\Security\Security;
58
use CodeIgniter\Session\Session;
59
use CodeIgniter\Superglobals;
60
use CodeIgniter\Throttle\Throttler;
61
use CodeIgniter\Typography\Typography;
62
use CodeIgniter\Validation\ValidatedInput;
63
use CodeIgniter\Validation\ValidationInterface;
64
use CodeIgniter\View\Cell;
65
use CodeIgniter\View\Parser;
66
use CodeIgniter\View\RendererInterface;
67
use CodeIgniter\View\View;
68
use Config\App;
69
use Config\Autoload;
70
use Config\Cache;
71
use Config\ContentSecurityPolicy as CSPConfig;
72
use Config\Encryption;
73
use Config\Exceptions as ConfigExceptions;
74
use Config\Filters as ConfigFilters;
75
use Config\Format as ConfigFormat;
76
use Config\Honeypot as ConfigHoneyPot;
77
use Config\Images;
78
use Config\Migrations;
79
use Config\Modules;
80
use Config\Optimize;
81
use Config\Pager as ConfigPager;
82
use Config\Services as AppServices;
83
use Config\Session as ConfigSession;
84
use Config\Toolbar as ConfigToolbar;
85
use Config\Validation as ConfigValidation;
86
use Config\View as ConfigView;
87
use Config\WorkerMode;
88

89
/**
90
 * Services Configuration file.
91
 *
92
 * Services are simply other classes/libraries that the system uses
93
 * to do its job. This is used by CodeIgniter to allow the core of the
94
 * framework to be swapped out easily without affecting the usage within
95
 * the rest of your application.
96
 *
97
 * This is used in place of a Dependency Injection container primarily
98
 * due to its simplicity, which allows a better long-term maintenance
99
 * of the applications built on top of CodeIgniter. A bonus side-effect
100
 * is that IDEs are able to determine what class you are calling
101
 * whereas with DI Containers there usually isn't a way for them to do this.
102
 *
103
 * Warning: To allow overrides by service providers do not use static calls,
104
 * instead call out to \Config\Services (imported as AppServices).
105
 *
106
 * @see http://blog.ircmaxell.com/2015/11/simple-easy-risk-and-change.html
107
 * @see http://www.infoq.com/presentations/Simple-Made-Easy
108
 *
109
 * @method static CacheInterface             cache(Cache $config = null, $getShared = true)
110
 * @method static CLIRequest                 clirequest(App $config = null, $getShared = true)
111
 * @method static CodeIgniter                codeigniter(App $config = null, $getShared = true)
112
 * @method static Commands                   commands($getShared = true)
113
 * @method static void                       createRequest(App $config, bool $isCli = false)
114
 * @method static ContentSecurityPolicy      csp(CSPConfig $config = null, $getShared = true)
115
 * @method static CURLRequest                curlrequest($options = [], ResponseInterface $response = null, App $config = null, $getShared = true)
116
 * @method static Email                      email($config = null, $getShared = true)
117
 * @method static EncrypterInterface         encrypter(Encryption $config = null, $getShared = false)
118
 * @method static EnvironmentDetector        environment(?string $environment = null, bool $getShared = true)
119
 * @method static Exceptions                 exceptions(ConfigExceptions $config = null, $getShared = true)
120
 * @method static Filters                    filters(ConfigFilters $config = null, $getShared = true)
121
 * @method static Format                     format(ConfigFormat $config = null, $getShared = true)
122
 * @method static Honeypot                   honeypot(ConfigHoneyPot $config = null, $getShared = true)
123
 * @method static BaseHandler                image($handler = null, Images $config = null, $getShared = true)
124
 * @method static IncomingRequest            incomingrequest(?App $config = null, bool $getShared = true)
125
 * @method static InputData                  inputdata(array<string, mixed> $data = [], bool $getShared = false)
126
 * @method static Iterator                   iterator($getShared = true)
127
 * @method static Language                   language($locale = null, $getShared = true)
128
 * @method static LockManager                locks(?CacheInterface $cache = null, bool $getShared = true)
129
 * @method static Logger                     logger($getShared = true)
130
 * @method static MigrationRunner            migrations(Migrations $config = null, ConnectionInterface $db = null, $getShared = true)
131
 * @method static Negotiate                  negotiator(RequestInterface $request = null, $getShared = true)
132
 * @method static Pager                      pager(ConfigPager $config = null, RendererInterface $view = null, $getShared = true)
133
 * @method static Parser                     parser($viewPath = null, ConfigView $config = null, $getShared = true)
134
 * @method static RedirectResponse           redirectresponse(App $config = null, $getShared = true)
135
 * @method static View                       renderer($viewPath = null, ConfigView $config = null, $getShared = true)
136
 * @method static IncomingRequest|CLIRequest request(App $config = null, $getShared = true)
137
 * @method static ResponseInterface          response(App $config = null, $getShared = true)
138
 * @method static ResponseCache              responsecache(?Cache $config = null, ?CacheInterface $cache = null, bool $getShared = true)
139
 * @method static Router                     router(RouteCollectionInterface $routes = null, Request $request = null, $getShared = true)
140
 * @method static RouteCollection            routes($getShared = true)
141
 * @method static Security                   security(App $config = null, $getShared = true)
142
 * @method static Session                    session(ConfigSession $config = null, $getShared = true)
143
 * @method static SiteURIFactory             siteurifactory(App $config = null, Superglobals $superglobals = null, $getShared = true)
144
 * @method static Superglobals               superglobals(array $server = null, array $get = null, bool $getShared = true)
145
 * @method static Throttler                  throttler($getShared = true)
146
 * @method static Timer                      timer($getShared = true)
147
 * @method static Toolbar                    toolbar(ConfigToolbar $config = null, $getShared = true)
148
 * @method static Typography                 typography($getShared = true)
149
 * @method static URI                        uri($uri = null, $getShared = true)
150
 * @method static ValidatedInput             validatedinput(array<string, mixed> $data = [], bool $getShared = false)
151
 * @method static ValidationInterface        validation(ConfigValidation $config = null, $getShared = true)
152
 * @method static Cell                       viewcell($getShared = true)
153
 */
154
class BaseService
155
{
156
    /**
157
     * Cache for instance of any services that
158
     * have been requested as a "shared" instance.
159
     * Keys should be lowercase service names.
160
     *
161
     * @var array<string, object> [key => instance]
162
     */
163
    protected static $instances = [];
164

165
    /**
166
     * Factory method list.
167
     *
168
     * @var array<string, (callable(mixed ...$params): object)> [key => callable]
169
     */
170
    protected static array $factories = [];
171

172
    /**
173
     * Mock objects for testing which are returned if exist.
174
     *
175
     * @var array<string, object> [key => instance]
176
     */
177
    protected static $mocks = [];
178

179
    /**
180
     * Have we already discovered other Services?
181
     *
182
     * @var bool
183
     */
184
    protected static $discovered = false;
185

186
    /**
187
     * A cache of the names of services classes found.
188
     *
189
     * @var list<string>
190
     */
191
    private static array $serviceNames = [];
192

193
    /**
194
     * Simple method to get an entry fast.
195
     *
196
     * @param string $key Identifier of the entry to look for.
197
     *
198
     * @return object|null Entry.
199
     */
200
    public static function get(string $key): ?object
201
    {
202
        return static::$instances[$key] ?? static::__callStatic($key, []);
7,735✔
203
    }
204

205
    /**
206
     * Checks if a service instance has been created.
207
     *
208
     * @param string $key Identifier of the entry to check.
209
     *
210
     * @return bool True if the service instance exists, false otherwise.
211
     */
212
    public static function has(string $key): bool
213
    {
214
        return isset(static::$instances[$key]);
78✔
215
    }
216

217
    /**
218
     * Sets an entry.
219
     *
220
     * @param string $key Identifier of the entry.
221
     */
222
    public static function set(string $key, object $value): void
223
    {
UNCOV
224
        if (isset(static::$instances[$key])) {
×
UNCOV
225
            throw new InvalidArgumentException('The entry for "' . $key . '" is already set.');
×
226
        }
227

UNCOV
228
        static::$instances[$key] = $value;
×
229
    }
230

231
    /**
232
     * Overrides an existing entry.
233
     *
234
     * @param string $key Identifier of the entry.
235
     */
236
    public static function override(string $key, object $value): void
237
    {
UNCOV
238
        static::$instances[$key] = $value;
×
239
    }
240

241
    /**
242
     * Returns a shared instance of any of the class' services.
243
     *
244
     * $key must be a name matching a service.
245
     *
246
     * @param array|bool|float|int|object|string|null ...$params
247
     *
248
     * @return object
249
     */
250
    protected static function getSharedInstance(string $key, ...$params)
251
    {
252
        $key = strtolower($key);
7,675✔
253

254
        // Returns mock if exists
255
        if (isset(static::$mocks[$key])) {
7,675✔
256
            return static::$mocks[$key];
510✔
257
        }
258

259
        if (! isset(static::$instances[$key])) {
7,675✔
260
            // Make sure $getShared is false
261
            $params[] = false;
2,242✔
262

263
            static::$instances[$key] = AppServices::$key(...$params);
2,242✔
264
        }
265

266
        return static::$instances[$key];
7,675✔
267
    }
268

269
    /**
270
     * The Autoloader class is the central class that handles our
271
     * spl_autoload_register method, and helper methods.
272
     *
273
     * @return Autoloader
274
     */
275
    public static function autoloader(bool $getShared = true)
276
    {
277
        if ($getShared) {
2,044✔
278
            if (empty(static::$instances['autoloader'])) {
2,041✔
279
                static::$instances['autoloader'] = new Autoloader();
2,034✔
280
            }
281

282
            return static::$instances['autoloader'];
2,041✔
283
        }
284

285
        return new Autoloader();
4✔
286
    }
287

288
    /**
289
     * The file locator provides utility methods for looking for non-classes
290
     * within namespaced folders, as well as convenience methods for
291
     * loading 'helpers', and 'libraries'.
292
     *
293
     * @return FileLocatorInterface
294
     */
295
    public static function locator(bool $getShared = true)
296
    {
297
        if ($getShared) {
2,414✔
298
            if (empty(static::$instances['locator'])) {
2,412✔
299
                $cacheEnabled = class_exists(Optimize::class)
1,908✔
300
                    && (new Optimize())->locatorCacheEnabled;
1,908✔
301

302
                if ($cacheEnabled) {
1,908✔
UNCOV
303
                    static::$instances['locator'] = new FileLocatorCached(new FileLocator(static::autoloader()));
×
304
                } else {
305
                    static::$instances['locator'] = new FileLocator(static::autoloader());
1,908✔
306
                }
307
            }
308

309
            return static::$mocks['locator'] ?? static::$instances['locator'];
2,412✔
310
        }
311

312
        return new FileLocator(static::autoloader());
3✔
313
    }
314

315
    /**
316
     * Provides the ability to perform case-insensitive calling of service
317
     * names.
318
     *
319
     * @return object|null
320
     */
321
    public static function __callStatic(string $name, array $arguments)
322
    {
323
        if (isset(static::$factories[$name])) {
7,675✔
324
            return static::$factories[$name](...$arguments);
7,675✔
325
        }
326

327
        $service = static::serviceExists($name);
2,237✔
328

329
        if ($service === null) {
2,237✔
330
            return null;
1✔
331
        }
332

333
        return $service::$name(...$arguments);
2,237✔
334
    }
335

336
    /**
337
     * Check if the requested service is defined and return the declaring
338
     * class. Return null if not found.
339
     */
340
    public static function serviceExists(string $name): ?string
341
    {
342
        static::buildServicesCache();
2,380✔
343

344
        $services = array_merge(self::$serviceNames, [Services::class]);
2,380✔
345
        $name     = strtolower($name);
2,380✔
346

347
        foreach ($services as $service) {
2,380✔
348
            if (method_exists($service, $name)) {
2,380✔
349
                static::$factories[$name] = [$service, $name];
2,379✔
350

351
                return $service;
2,379✔
352
            }
353
        }
354

355
        return null;
2✔
356
    }
357

358
    /**
359
     * Reset shared instances and mocks for testing.
360
     *
361
     * @return void
362
     *
363
     * @testTag only available to test code
364
     */
365
    public static function reset(bool $initAutoloader = true)
366
    {
367
        static::$mocks     = [];
2,034✔
368
        static::$instances = [];
2,034✔
369
        static::$factories = [];
2,034✔
370

371
        if ($initAutoloader) {
2,034✔
372
            static::autoloader()->initialize(new Autoload(), new Modules());
2,034✔
373
        }
374
    }
375

376
    /**
377
     * Reconnect cache connection for worker mode at the start of a request.
378
     * Checks if cache connection is alive and reconnects if needed.
379
     *
380
     * This should be called at the beginning of each request in worker mode,
381
     * before the application runs.
382
     */
383
    public static function reconnectCacheForWorkerMode(): void
384
    {
385
        if (! isset(static::$instances['cache'])) {
2✔
386
            return;
1✔
387
        }
388

389
        $cache = static::$instances['cache'];
1✔
390

391
        if (! $cache->ping()) {
1✔
UNCOV
392
            $cache->reconnect();
×
393
        }
394
    }
395

396
    /**
397
     * Resets all services except those in the persistent list.
398
     * Used for worker mode to preserve expensive-to-initialize services.
399
     *
400
     * Called at the END of each request to clean up state.
401
     */
402
    public static function resetForWorkerMode(WorkerMode $config): void
403
    {
404
        // Reset mocks (testing only, safe to clear)
405
        static::$mocks = [];
1✔
406

407
        // Reset factories
408
        static::$factories = [];
1✔
409

410
        // Process each service instance
411
        $persistentInstances = [];
1✔
412

413
        foreach (static::$instances as $serviceName => $service) {
1✔
414
            // Persist services in the persistent list
415
            if (in_array($serviceName, $config->persistentServices, true)) {
1✔
416
                $persistentInstances[$serviceName] = $service;
1✔
417
            }
418
        }
419

420
        static::$instances = $persistentInstances;
1✔
421
    }
422

423
    /**
424
     * Resets any mock and shared instances for a single service.
425
     *
426
     * @return void
427
     *
428
     * @testTag only available to test code
429
     */
430
    public static function resetSingle(string $name)
431
    {
432
        $name = strtolower($name);
80✔
433
        unset(static::$mocks[$name], static::$instances[$name]);
80✔
434
    }
435

436
    /**
437
     * Inject mock object for testing.
438
     *
439
     * @param object $mock
440
     *
441
     * @return void
442
     *
443
     * @testTag only available to test code
444
     */
445
    public static function injectMock(string $name, $mock)
446
    {
447
        static::$instances[$name]         = $mock;
7,673✔
448
        static::$mocks[strtolower($name)] = $mock;
7,673✔
449
    }
450

451
    /**
452
     * Resets the service cache.
453
     */
454
    public static function resetServicesCache(): void
455
    {
456
        self::$serviceNames = [];
1✔
457
        static::$discovered = false;
1✔
458
    }
459

460
    protected static function buildServicesCache(): void
461
    {
462
        if (! static::$discovered) {
2,380✔
463
            if ((new Modules())->shouldDiscover('services')) {
1✔
464
                $locator = static::locator();
1✔
465
                $files   = $locator->search('Config/Services');
1✔
466

467
                $systemPath = static::autoloader()->getNamespace('CodeIgniter')[0];
1✔
468

469
                // Get instances of all service classes and cache them locally.
470
                foreach ($files as $file) {
1✔
471
                    // Does not search `CodeIgniter` namespace to prevent from loading twice.
472
                    if (str_starts_with($file, $systemPath)) {
1✔
473
                        continue;
1✔
474
                    }
475

476
                    $classname = $locator->findQualifiedNameFromPath($file);
1✔
477

478
                    if ($classname === false) {
1✔
UNCOV
479
                        continue;
×
480
                    }
481

482
                    if ($classname !== Services::class) {
1✔
483
                        self::$serviceNames[] = $classname;
1✔
484
                    }
485
                }
486
            }
487

488
            static::$discovered = true;
1✔
489
        }
490
    }
491
}
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