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

codeigniter4 / CodeIgniter4 / 25410067699

06 May 2026 12:34AM UTC coverage: 88.279% (+0.005%) from 88.274%
25410067699

Pull #10158

github

web-flow
Merge 06a737a4b into 3cdb4c5e1
Pull Request #10158: feat: add typed FormRequest accessors

64 of 71 new or added lines in 3 files covered. (90.14%)

28 existing lines in 4 files now uncovered.

23551 of 26678 relevant lines covered (88.28%)

218.05 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\Language\Language;
50
use CodeIgniter\Lock\LockManager;
51
use CodeIgniter\Log\Logger;
52
use CodeIgniter\Pager\Pager;
53
use CodeIgniter\Router\RouteCollection;
54
use CodeIgniter\Router\RouteCollectionInterface;
55
use CodeIgniter\Router\Router;
56
use CodeIgniter\Security\Security;
57
use CodeIgniter\Session\Session;
58
use CodeIgniter\Superglobals;
59
use CodeIgniter\Throttle\Throttler;
60
use CodeIgniter\Typography\Typography;
61
use CodeIgniter\Validation\ValidationInterface;
62
use CodeIgniter\View\Cell;
63
use CodeIgniter\View\Parser;
64
use CodeIgniter\View\RendererInterface;
65
use CodeIgniter\View\View;
66
use Config\App;
67
use Config\Autoload;
68
use Config\Cache;
69
use Config\ContentSecurityPolicy as CSPConfig;
70
use Config\Encryption;
71
use Config\Exceptions as ConfigExceptions;
72
use Config\Filters as ConfigFilters;
73
use Config\Format as ConfigFormat;
74
use Config\Honeypot as ConfigHoneyPot;
75
use Config\Images;
76
use Config\Migrations;
77
use Config\Modules;
78
use Config\Optimize;
79
use Config\Pager as ConfigPager;
80
use Config\Services as AppServices;
81
use Config\Session as ConfigSession;
82
use Config\Toolbar as ConfigToolbar;
83
use Config\Validation as ConfigValidation;
84
use Config\View as ConfigView;
85
use Config\WorkerMode;
86

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

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

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

175
    /**
176
     * Have we already discovered other Services?
177
     *
178
     * @var bool
179
     */
180
    protected static $discovered = false;
181

182
    /**
183
     * A cache of the names of services classes found.
184
     *
185
     * @var list<string>
186
     */
187
    private static array $serviceNames = [];
188

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

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

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

UNCOV
224
        static::$instances[$key] = $value;
×
225
    }
226

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

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

250
        // Returns mock if exists
251
        if (isset(static::$mocks[$key])) {
7,612✔
252
            return static::$mocks[$key];
506✔
253
        }
254

255
        if (! isset(static::$instances[$key])) {
7,612✔
256
            // Make sure $getShared is false
257
            $params[] = false;
2,224✔
258

259
            static::$instances[$key] = AppServices::$key(...$params);
2,224✔
260
        }
261

262
        return static::$instances[$key];
7,612✔
263
    }
264

265
    /**
266
     * The Autoloader class is the central class that handles our
267
     * spl_autoload_register method, and helper methods.
268
     *
269
     * @return Autoloader
270
     */
271
    public static function autoloader(bool $getShared = true)
272
    {
273
        if ($getShared) {
2,026✔
274
            if (empty(static::$instances['autoloader'])) {
2,023✔
275
                static::$instances['autoloader'] = new Autoloader();
2,016✔
276
            }
277

278
            return static::$instances['autoloader'];
2,023✔
279
        }
280

281
        return new Autoloader();
4✔
282
    }
283

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

298
                if ($cacheEnabled) {
1,892✔
UNCOV
299
                    static::$instances['locator'] = new FileLocatorCached(new FileLocator(static::autoloader()));
×
300
                } else {
301
                    static::$instances['locator'] = new FileLocator(static::autoloader());
1,892✔
302
                }
303
            }
304

305
            return static::$mocks['locator'] ?? static::$instances['locator'];
2,396✔
306
        }
307

308
        return new FileLocator(static::autoloader());
3✔
309
    }
310

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

323
        $service = static::serviceExists($name);
2,218✔
324

325
        if ($service === null) {
2,218✔
326
            return null;
1✔
327
        }
328

329
        return $service::$name(...$arguments);
2,218✔
330
    }
331

332
    /**
333
     * Check if the requested service is defined and return the declaring
334
     * class. Return null if not found.
335
     */
336
    public static function serviceExists(string $name): ?string
337
    {
338
        static::buildServicesCache();
2,357✔
339

340
        $services = array_merge(self::$serviceNames, [Services::class]);
2,357✔
341
        $name     = strtolower($name);
2,357✔
342

343
        foreach ($services as $service) {
2,357✔
344
            if (method_exists($service, $name)) {
2,357✔
345
                static::$factories[$name] = [$service, $name];
2,356✔
346

347
                return $service;
2,356✔
348
            }
349
        }
350

351
        return null;
2✔
352
    }
353

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

367
        if ($initAutoloader) {
2,016✔
368
            static::autoloader()->initialize(new Autoload(), new Modules());
2,016✔
369
        }
370
    }
371

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

385
        $cache = static::$instances['cache'];
1✔
386

387
        if (! $cache->ping()) {
1✔
UNCOV
388
            $cache->reconnect();
×
389
        }
390
    }
391

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

403
        // Reset factories
404
        static::$factories = [];
1✔
405

406
        // Process each service instance
407
        $persistentInstances = [];
1✔
408

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

416
        static::$instances = $persistentInstances;
1✔
417
    }
418

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

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

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

456
    protected static function buildServicesCache(): void
457
    {
458
        if (! static::$discovered) {
2,357✔
459
            if ((new Modules())->shouldDiscover('services')) {
1✔
460
                $locator = static::locator();
1✔
461
                $files   = $locator->search('Config/Services');
1✔
462

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

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

472
                    $classname = $locator->findQualifiedNameFromPath($file);
1✔
473

474
                    if ($classname === false) {
1✔
UNCOV
475
                        continue;
×
476
                    }
477

478
                    if ($classname !== Services::class) {
1✔
479
                        self::$serviceNames[] = $classname;
1✔
480
                    }
481
                }
482
            }
483

484
            static::$discovered = true;
1✔
485
        }
486
    }
487
}
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