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

codeigniter4 / CodeIgniter4 / 8678236145

14 Apr 2024 04:02AM UTC coverage: 84.44% (-0.01%) from 84.451%
8678236145

push

github

web-flow
Merge pull request #8782 from kenjis/release-4.5.1

Prep for 4.5.1 release

20318 of 24062 relevant lines covered (84.44%)

188.23 hits per line

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

71.43
/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\Filters\Filters;
33
use CodeIgniter\Format\Format;
34
use CodeIgniter\Honeypot\Honeypot;
35
use CodeIgniter\HTTP\CLIRequest;
36
use CodeIgniter\HTTP\ContentSecurityPolicy;
37
use CodeIgniter\HTTP\CURLRequest;
38
use CodeIgniter\HTTP\IncomingRequest;
39
use CodeIgniter\HTTP\Negotiate;
40
use CodeIgniter\HTTP\RedirectResponse;
41
use CodeIgniter\HTTP\Request;
42
use CodeIgniter\HTTP\RequestInterface;
43
use CodeIgniter\HTTP\ResponseInterface;
44
use CodeIgniter\HTTP\SiteURIFactory;
45
use CodeIgniter\HTTP\URI;
46
use CodeIgniter\Images\Handlers\BaseHandler;
47
use CodeIgniter\Language\Language;
48
use CodeIgniter\Log\Logger;
49
use CodeIgniter\Pager\Pager;
50
use CodeIgniter\Router\RouteCollection;
51
use CodeIgniter\Router\RouteCollectionInterface;
52
use CodeIgniter\Router\Router;
53
use CodeIgniter\Security\Security;
54
use CodeIgniter\Session\Session;
55
use CodeIgniter\Superglobals;
56
use CodeIgniter\Throttle\Throttler;
57
use CodeIgniter\Typography\Typography;
58
use CodeIgniter\Validation\ValidationInterface;
59
use CodeIgniter\View\Cell;
60
use CodeIgniter\View\Parser;
61
use CodeIgniter\View\RendererInterface;
62
use CodeIgniter\View\View;
63
use Config\App;
64
use Config\Autoload;
65
use Config\Cache;
66
use Config\ContentSecurityPolicy as CSPConfig;
67
use Config\Encryption;
68
use Config\Exceptions as ConfigExceptions;
69
use Config\Filters as ConfigFilters;
70
use Config\Format as ConfigFormat;
71
use Config\Honeypot as ConfigHoneyPot;
72
use Config\Images;
73
use Config\Migrations;
74
use Config\Modules;
75
use Config\Optimize;
76
use Config\Pager as ConfigPager;
77
use Config\Services as AppServices;
78
use Config\Toolbar as ConfigToolbar;
79
use Config\Validation as ConfigValidation;
80
use Config\View as ConfigView;
81
use InvalidArgumentException;
82

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

155
    /**
156
     * Factory method list.
157
     *
158
     * @var array<string, (callable(mixed ...$params): object)> [key => callable]
159
     */
160
    protected static array $factories = [];
161

162
    /**
163
     * Mock objects for testing which are returned if exist.
164
     *
165
     * @var array<string, object> [key => instance]
166
     */
167
    protected static $mocks = [];
168

169
    /**
170
     * Have we already discovered other Services?
171
     *
172
     * @var bool
173
     */
174
    protected static $discovered = false;
175

176
    /**
177
     * A cache of other service classes we've found.
178
     *
179
     * @var array
180
     *
181
     * @deprecated 4.5.0 No longer used.
182
     */
183
    protected static $services = [];
184

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

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

204
    /**
205
     * Sets an entry.
206
     *
207
     * @param string $key Identifier of the entry.
208
     */
209
    public static function set(string $key, object $value): void
210
    {
211
        if (isset(static::$instances[$key])) {
×
212
            throw new InvalidArgumentException('The entry for "' . $key . '" is already set.');
×
213
        }
214

215
        static::$instances[$key] = $value;
×
216
    }
217

218
    /**
219
     * Overrides an existing entry.
220
     *
221
     * @param string $key Identifier of the entry.
222
     */
223
    public static function override(string $key, object $value): void
224
    {
225
        static::$instances[$key] = $value;
×
226
    }
227

228
    /**
229
     * Returns a shared instance of any of the class' services.
230
     *
231
     * $key must be a name matching a service.
232
     *
233
     * @param array|bool|float|int|object|string|null ...$params
234
     *
235
     * @return object
236
     */
237
    protected static function getSharedInstance(string $key, ...$params)
238
    {
239
        $key = strtolower($key);
6,370✔
240

241
        // Returns mock if exists
242
        if (isset(static::$mocks[$key])) {
6,370✔
243
            return static::$mocks[$key];
123✔
244
        }
245

246
        if (! isset(static::$instances[$key])) {
6,370✔
247
            // Make sure $getShared is false
248
            $params[] = false;
1,820✔
249

250
            static::$instances[$key] = AppServices::$key(...$params);
1,820✔
251
        }
252

253
        return static::$instances[$key];
6,370✔
254
    }
255

256
    /**
257
     * The Autoloader class is the central class that handles our
258
     * spl_autoload_register method, and helper methods.
259
     *
260
     * @return Autoloader
261
     */
262
    public static function autoloader(bool $getShared = true)
263
    {
264
        if ($getShared) {
2,075✔
265
            if (empty(static::$instances['autoloader'])) {
2,072✔
266
                static::$instances['autoloader'] = new Autoloader();
1,644✔
267
            }
268

269
            return static::$instances['autoloader'];
2,072✔
270
        }
271

272
        return new Autoloader();
4✔
273
    }
274

275
    /**
276
     * The file locator provides utility methods for looking for non-classes
277
     * within namespaced folders, as well as convenience methods for
278
     * loading 'helpers', and 'libraries'.
279
     *
280
     * @return FileLocatorInterface
281
     */
282
    public static function locator(bool $getShared = true)
283
    {
284
        if ($getShared) {
2,220✔
285
            if (empty(static::$instances['locator'])) {
2,218✔
286
                $cacheEnabled = class_exists(Optimize::class)
1,546✔
287
                    && (new Optimize())->locatorCacheEnabled;
1,546✔
288

289
                if ($cacheEnabled) {
1,546✔
290
                    static::$instances['locator'] = new FileLocatorCached(new FileLocator(static::autoloader()));
×
291
                } else {
292
                    static::$instances['locator'] = new FileLocator(static::autoloader());
1,546✔
293
                }
294
            }
295

296
            return static::$mocks['locator'] ?? static::$instances['locator'];
2,218✔
297
        }
298

299
        return new FileLocator(static::autoloader());
3✔
300
    }
301

302
    /**
303
     * Provides the ability to perform case-insensitive calling of service
304
     * names.
305
     *
306
     * @return object|null
307
     */
308
    public static function __callStatic(string $name, array $arguments)
309
    {
310
        if (isset(static::$factories[$name])) {
6,373✔
311
            return static::$factories[$name](...$arguments);
6,373✔
312
        }
313

314
        $service = static::serviceExists($name);
1,826✔
315

316
        if ($service === null) {
1,826✔
317
            return null;
1✔
318
        }
319

320
        return $service::$name(...$arguments);
1,826✔
321
    }
322

323
    /**
324
     * Check if the requested service is defined and return the declaring
325
     * class. Return null if not found.
326
     */
327
    public static function serviceExists(string $name): ?string
328
    {
329
        static::buildServicesCache();
1,898✔
330

331
        $services = array_merge(self::$serviceNames, [Services::class]);
1,898✔
332
        $name     = strtolower($name);
1,898✔
333

334
        foreach ($services as $service) {
1,898✔
335
            if (method_exists($service, $name)) {
1,898✔
336
                static::$factories[$name] = [$service, $name];
1,897✔
337

338
                return $service;
1,897✔
339
            }
340
        }
341

342
        return null;
2✔
343
    }
344

345
    /**
346
     * Reset shared instances and mocks for testing.
347
     *
348
     * @return void
349
     */
350
    public static function reset(bool $initAutoloader = true)
351
    {
352
        static::$mocks     = [];
1,644✔
353
        static::$instances = [];
1,644✔
354
        static::$factories = [];
1,644✔
355

356
        if ($initAutoloader) {
1,644✔
357
            static::autoloader()->initialize(new Autoload(), new Modules());
1,644✔
358
        }
359
    }
360

361
    /**
362
     * Resets any mock and shared instances for a single service.
363
     *
364
     * @return void
365
     */
366
    public static function resetSingle(string $name)
367
    {
368
        $name = strtolower($name);
2✔
369
        unset(static::$mocks[$name], static::$instances[$name]);
2✔
370
    }
371

372
    /**
373
     * Inject mock object for testing.
374
     *
375
     * @param object $mock
376
     *
377
     * @return void
378
     */
379
    public static function injectMock(string $name, $mock)
380
    {
381
        static::$instances[$name]         = $mock;
6,367✔
382
        static::$mocks[strtolower($name)] = $mock;
6,367✔
383
    }
384

385
    protected static function buildServicesCache(): void
386
    {
387
        if (! static::$discovered) {
1,898✔
388
            if ((new Modules())->shouldDiscover('services')) {
×
389
                $locator = static::locator();
×
390
                $files   = $locator->search('Config/Services');
×
391

392
                $systemPath = static::autoloader()->getNamespace('CodeIgniter')[0];
×
393

394
                // Get instances of all service classes and cache them locally.
395
                foreach ($files as $file) {
×
396
                    // Does not search `CodeIgniter` namespace to prevent from loading twice.
397
                    if (str_starts_with($file, $systemPath)) {
×
398
                        continue;
×
399
                    }
400

401
                    $classname = $locator->findQualifiedNameFromPath($file);
×
402

403
                    if ($classname === false) {
×
404
                        continue;
×
405
                    }
406

407
                    if ($classname !== Services::class) {
×
408
                        self::$serviceNames[] = $classname;
×
409
                    }
410
                }
411
            }
412

413
            static::$discovered = true;
×
414
        }
415
    }
416
}
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

© 2025 Coveralls, Inc