Test Failed
Push — develop ( afcd85...c5c862 )
by nguereza
03:41
created

Application::createServiceProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file Application.php
34
 *
35
 *  The Platine Application class
36
 *
37
 *  @package    Platine\Framework\App
38
 *  @author Platine Developers team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   http://www.iacademy.cf
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Framework\App;
49
50
use InvalidArgumentException;
51
use Platine\Config\Config;
52
use Platine\Config\FileLoader;
53
use Platine\Container\Container;
54
use Platine\Event\DispatcherInterface;
55
use Platine\Event\EventInterface;
56
use Platine\Event\ListenerInterface;
57
use Platine\Event\SubscriberInterface;
58
use Platine\Framework\Service\Provider\BaseServiceProvider;
59
use Platine\Framework\Service\Provider\EventServiceProvider;
60
use Platine\Framework\Service\ServiceProvider;
61
62
/**
63
 * @class Application
64
 * @package Platine\Framework\App
65
 */
66
class Application extends Container
67
{
68
69
    /**
70
     * The application version
71
     */
72
    public const VERSION = '1.0.0-dev';
73
74
    /**
75
     * The event dispatcher instance
76
     * @var DispatcherInterface
77
     */
78
    protected DispatcherInterface $dispatcher;
79
80
    /**
81
     * The base path for this application
82
     * @var string
83
     */
84
    protected string $basePath = '';
85
86
    /**
87
     * The vendor path
88
     * @var string
89
     */
90
    protected string $vendorPath = '';
91
92
    /**
93
     * The Application path
94
     * @var string
95
     */
96
    protected string $appPath = '';
97
98
    /**
99
     * The application configuration path
100
     * @var string
101
     */
102
    protected string $configPath = 'config';
103
104
    /**
105
     * The application storage path
106
     * @var string
107
     */
108
    protected string $storagePath = 'storage';
109
110
    /**
111
     * The custom app name space
112
     * @var string
113
     */
114
    protected string $namespace = '';
115
116
    /**
117
     * The list of service providers
118
     * @var array<string, ServiceProvider>
119
     */
120
    protected array $providers = [];
121
122
    /**
123
     * Whether the system already booted
124
     * @var bool
125
     */
126
    protected bool $booted = false;
127
128
    /**
129
     * The application environment
130
     * @var string
131
     */
132
    protected string $env = 'dev';
133
134
    /**
135
     * Create new instance
136
     * @param string $basePath
137
     */
138
    public function __construct(string $basePath = '')
139
    {
140
        parent::__construct();
141
        $this->basePath = $basePath;
142
        $this->loadCoreServiceProviders();
143
144
        $this->dispatcher = $this->get(DispatcherInterface::class);
145
    }
146
147
    /**
148
     * Return the application version
149
     * @return string
150
     */
151
    public function version(): string
152
    {
153
        return self::VERSION;
154
    }
155
156
    /**
157
     * Return the application name space
158
     * @return string
159
     */
160
    public function getNamespace(): string
161
    {
162
        return $this->namespace;
163
    }
164
165
    /**
166
     * Set the application name space
167
     * @param string $namespace
168
     * @return $this
169
     */
170
    public function setNamespace(string $namespace): self
171
    {
172
        $this->namespace = $namespace;
173
        return $this;
174
    }
175
176
    /**
177
     * Return the current environment
178
     * @return string
179
     */
180
    public function getEnvironment(): string
181
    {
182
        return $this->env;
183
    }
184
185
    /**
186
     * Set the environment
187
     * @param string $env
188
     * @return $this
189
     */
190
    public function setEnvironment(string $env)
191
    {
192
        $this->env = $env;
193
194
        return $this;
195
    }
196
197
    /**
198
     * Return the storage path
199
     * @return string
200
     */
201
    public function getStoragePath(): string
202
    {
203
        return $this->storagePath;
204
    }
205
206
    /**
207
     * Set the storage path
208
     * @param string $storagePath
209
     * @return $this
210
     */
211
    public function setStoragePath(string $storagePath): self
212
    {
213
        $this->storagePath = $storagePath;
214
215
        return $this;
216
    }
217
218
219
    /**
220
     * Return the vendor path
221
     * @return string
222
     */
223
    public function getVendorPath(): string
224
    {
225
        return $this->vendorPath;
226
    }
227
228
    /**
229
     * Set vendor path
230
     * @param string $vendorPath
231
     * @return $this
232
     */
233
    public function setVendorPath(string $vendorPath): self
234
    {
235
        $this->vendorPath = $vendorPath;
236
237
        return $this;
238
    }
239
240
     /**
241
     * Return the application root path
242
     * @return string
243
     */
244
    public function getAppPath(): string
245
    {
246
        return $this->appPath;
247
    }
248
249
    /**
250
     * Set Application path
251
     * @param string $appPath
252
     * @return $this
253
     */
254
    public function setAppPath(string $appPath): self
255
    {
256
        $this->appPath = $appPath;
257
258
        return $this;
259
    }
260
261
    /**
262
     * Return the application base path
263
     * @return string
264
     */
265
    public function getBasePath(): string
266
    {
267
        return $this->basePath;
268
    }
269
270
    /**
271
     * Set the application base path
272
     * @param string $basePath
273
     * @return $this
274
     */
275
    public function setBasePath(string $basePath): self
276
    {
277
        $this->basePath = $basePath;
278
279
        return $this;
280
    }
281
282
    /**
283
     * Return the application configuration path
284
     * @return string
285
     */
286
    public function getConfigPath(): string
287
    {
288
        return $this->configPath;
289
    }
290
291
    /**
292
     * Set the application configuration path
293
     * @param string $configPath
294
     * @return $this
295
     */
296
    public function setConfigPath(string $configPath): self
297
    {
298
        $this->configPath = $configPath;
299
300
        return $this;
301
    }
302
303
    /**
304
     * Dispatches an event to all registered listeners.
305
     * @param  string|EventInterface $eventName the name of event
306
     * of instance of EventInterface
307
     * @param  EventInterface|null $event  the instance of EventInterface or null
308
     * @return $this
309
     */
310
    public function dispatch($eventName, EventInterface $event = null): self
311
    {
312
        $this->dispatcher->dispatch($eventName, $event);
313
314
        return $this;
315
    }
316
317
    /**
318
     * Register a listener for the given event.
319
     *
320
     * @param string $eventName the name of event
321
     * @param ListenerInterface|callable|string $listener the Listener
322
     * interface or any callable
323
     * @param int $priority the listener execution priority
324
     * @return $this
325
     */
326
    public function listen(
327
        string $eventName,
328
        $listener,
329
        int $priority = DispatcherInterface::PRIORITY_DEFAULT
330
    ): self {
331
        if (is_string($listener)) {
332
            $listener = $this->createListener($listener);
333
        }
334
        $this->dispatcher->addListener($eventName, $listener, $priority);
335
336
        return $this;
337
    }
338
339
    /**
340
     * Add event subscriber
341
     * @param SubscriberInterface $subscriber
342
     * @return $this
343
     */
344
    public function subscribe(SubscriberInterface $subscriber): self
345
    {
346
        $this->dispatcher->addSubscriber($subscriber);
347
348
        return $this;
349
    }
350
351
    /**
352
     * Return the list of providers
353
     * @return array<string, ServiceProvider>
354
     */
355
    public function getProviders(): array
356
    {
357
        return $this->providers;
358
    }
359
360
    /**
361
     * Return the list of service providers commands
362
     * @return array<class-string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string>.
Loading history...
363
     */
364
    public function getProvidersCommands(): array
365
    {
366
        $commands = [];
367
        foreach ($this->providers as /** @var ServiceProvider $provider */ $provider) {
368
            $commands = array_merge($commands, $provider->getCommands());
369
        }
370
371
        return $commands;
372
    }
373
374
    /**
375
     * Boot the application
376
     * @return void
377
     */
378
    public function boot(): void
379
    {
380
        if ($this->booted) {
381
            return;
382
        }
383
384
        foreach ($this->providers as $provider) {
385
            $this->bootServiceProvider($provider);
386
        }
387
388
        $this->booted = true;
389
    }
390
391
    /**
392
     * Register the service provider
393
     * @param string|ServiceProvider $provider
394
     * @param bool $force whether to force registration of provider
395
     * if already loaded
396
     * @return ServiceProvider
397
     */
398
    public function registerServiceProvider(
399
        $provider,
400
        bool $force = false
401
    ): ServiceProvider {
402
        $registered = $this->getServiceProvider($provider);
403
        if ($registered && !$force) {
404
            return $registered;
405
        }
406
407
        if (is_string($provider)) {
408
            $provider = $this->createServiceProvider($provider);
409
        }
410
411
        $provider->register();
412
413
        $this->markProviderAsRegistered($provider);
414
415
        if ($this->booted) {
416
            $this->bootServiceProvider($provider);
417
        }
418
419
        return $provider;
420
    }
421
422
    /**
423
     * Return the registered service provider if exist
424
     * @param string|ServiceProvider $provider
425
     * @return ServiceProvider|null
426
     */
427
    public function getServiceProvider($provider): ?ServiceProvider
428
    {
429
        $name = is_string($provider)
430
                ? $provider
431
                : get_class($provider);
432
433
        return $this->providers[$name] ?? null;
434
    }
435
436
    /**
437
     * Load configured service providers
438
     * @return void
439
     */
440
    public function registerConfiguredServiceProviders(): void
441
    {
442
        /** @var Config<T> $config */
443
        $config = $this->get(Config::class);
444
445
        /** @var string[] $providers */
446
        $providers = $config->get('providers', []);
447
        foreach ($providers as $provider) {
448
            $this->registerServiceProvider($provider);
449
        }
450
    }
451
452
    /**
453
     * Load configured events and listeners
454
     * @return void
455
     */
456
    public function registerConfiguredEvents(): void
457
    {
458
        /** @var Config<T> $config */
459
        $config = $this->get(Config::class);
460
461
        /** @var array<string, string[]> $events */
462
        $events = $config->get('events', []);
463
        foreach ($events as $eventName => $listeners) {
464
            foreach ($listeners as $listener) {
465
                $this->listen($eventName, $listener);
466
            }
467
        }
468
    }
469
470
    /**
471
     * Load the application configuration
472
     * @return void
473
     */
474
    public function registerConfiguration(): void
475
    {
476
        $loader = new FileLoader($this->getConfigPath());
477
        $config = new Config($loader, $this->env);
478
        $this->instance($loader);
479
        $this->instance($config);
480
481
        date_default_timezone_set($config->get('app.timezone', 'UTC'));
482
    }
483
484
    /**
485
     * Create service provider
486
     * @param string $provider
487
     * @return ServiceProvider
488
     */
489
    protected function createServiceProvider(string $provider): ServiceProvider
490
    {
491
        return new $provider($this);
492
    }
493
494
    /**
495
     * Boot the given service provider
496
     * @param ServiceProvider $provider
497
     * @return void
498
     */
499
    protected function bootServiceProvider(ServiceProvider $provider): void
500
    {
501
        $provider->boot();
502
    }
503
504
    /**
505
     * Set the given service provider as registered
506
     * @param ServiceProvider $provider
507
     * @return void
508
     */
509
    protected function markProviderAsRegistered(ServiceProvider $provider): void
510
    {
511
        $this->providers[get_class($provider)] = $provider;
512
    }
513
514
    /**
515
     * Load framework core service providers
516
     * @return void
517
     */
518
    protected function loadCoreServiceProviders(): void
519
    {
520
        $this->registerServiceProvider(new BaseServiceProvider($this));
521
        $this->registerServiceProvider(new EventServiceProvider($this));
522
    }
523
524
    /**
525
     * Create listener using the container or direct class instance
526
     * @param string $listener
527
     * @return ListenerInterface
528
     */
529
    protected function createListener(string $listener): ListenerInterface
530
    {
531
        if ($this->has($listener)) {
532
            return $this->get($listener);
533
        }
534
535
        if (class_exists($listener)) {
536
            return new $listener();
537
        }
538
539
        throw new InvalidArgumentException(sprintf(
540
            'Can not resolve the listener class [%s], check if this is the'
541
                . ' identifier of container or class exists',
542
            $listener
543
        ));
544
    }
545
}
546