Passed
Push — develop ( 5bfa50...7a6395 )
by nguereza
02:37
created

Application::loadCoreServiceProviders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 4
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\Env\Loader;
59
use Platine\Framework\Service\Provider\BaseServiceProvider;
60
use Platine\Framework\Service\Provider\EventServiceProvider;
61
use Platine\Framework\Service\ServiceProvider;
62
use Platine\Stdlib\Helper\Path;
63
64
/**
65
 * @class Application
66
 * @package Platine\Framework\App
67
 */
68
class Application extends Container
69
{
70
71
    /**
72
     * The application version
73
     */
74
    public const VERSION = '1.0.0-dev';
75
76
    /**
77
     * The event dispatcher instance
78
     * @var DispatcherInterface
79
     */
80
    protected DispatcherInterface $dispatcher;
81
82
    /**
83
     * The base path for this application
84
     * @var string
85
     */
86
    protected string $basePath = '';
87
88
    /**
89
     * The vendor path
90
     * @var string
91
     */
92
    protected string $vendorPath = '';
93
94
    /**
95
     * The Application path
96
     * @var string
97
     */
98
    protected string $appPath = '';
99
100
    /**
101
     * The application root path
102
     * @var string
103
     */
104
    protected string $rootPath = '';
105
106
    /**
107
     * The application configuration path
108
     * @var string
109
     */
110
    protected string $configPath = 'config';
111
112
    /**
113
     * The application storage path
114
     * @var string
115
     */
116
    protected string $storagePath = 'storage';
117
118
    /**
119
     * The custom app name space
120
     * @var string
121
     */
122
    protected string $namespace = '';
123
124
    /**
125
     * The list of service providers
126
     * @var array<string, ServiceProvider>
127
     */
128
    protected array $providers = [];
129
130
    /**
131
     * Whether the system already booted
132
     * @var bool
133
     */
134
    protected bool $booted = false;
135
136
    /**
137
     * The application environment
138
     * @var string
139
     */
140
    protected string $env = 'dev';
141
142
    /**
143
     * The environment file name
144
     * @var string
145
     */
146
    protected string $environmentFile = '.env';
147
148
    /**
149
     * Create new instance
150
     * @param string $basePath
151
     */
152
    public function __construct(string $basePath = '')
153
    {
154
        parent::__construct();
155
156
        $this->basePath = $basePath;
157
        $this->loadCoreServiceProviders();
158
159
        $this->dispatcher = $this->get(DispatcherInterface::class);
160
    }
161
162
    /**
163
     * Return the application version
164
     * @return string
165
     */
166
    public function version(): string
167
    {
168
        return self::VERSION;
169
    }
170
171
    /**
172
     * Return the environment file
173
     * @return string
174
     */
175
    public function getEnvironmentFile(): string
176
    {
177
        return $this->environmentFile;
178
    }
179
180
181
    /**
182
     * Set the environment file
183
     * @param string $environmentFile
184
     * @return $this
185
     */
186
    public function setEnvironmentFile(string $environmentFile): self
187
    {
188
        $this->environmentFile = $environmentFile;
189
190
        return $this;
191
    }
192
193
    /**
194
     * Return the root path
195
     * @return string
196
     */
197
    public function getRootPath(): string
198
    {
199
        return $this->rootPath;
200
    }
201
202
    /**
203
     * Set root path
204
     * @param string $rootPath
205
     * @return $this
206
     */
207
    public function setRootPath(string $rootPath): self
208
    {
209
        $this->rootPath = $rootPath;
210
211
        return $this;
212
    }
213
214
    /**
215
     * Return the application name space
216
     * @return string
217
     */
218
    public function getNamespace(): string
219
    {
220
        return $this->namespace;
221
    }
222
223
    /**
224
     * Set the application name space
225
     * @param string $namespace
226
     * @return $this
227
     */
228
    public function setNamespace(string $namespace): self
229
    {
230
        $this->namespace = $namespace;
231
        return $this;
232
    }
233
234
    /**
235
     * Return the current environment
236
     * @return string
237
     */
238
    public function getEnvironment(): string
239
    {
240
        return $this->env;
241
    }
242
243
    /**
244
     * Set the environment
245
     * @param string $env
246
     * @return $this
247
     */
248
    public function setEnvironment(string $env)
249
    {
250
        $this->env = $env;
251
252
        return $this;
253
    }
254
255
    /**
256
     * Return the storage path
257
     * @return string
258
     */
259
    public function getStoragePath(): string
260
    {
261
        return $this->storagePath;
262
    }
263
264
    /**
265
     * Set the storage path
266
     * @param string $storagePath
267
     * @return $this
268
     */
269
    public function setStoragePath(string $storagePath): self
270
    {
271
        $this->storagePath = $storagePath;
272
273
        return $this;
274
    }
275
276
277
    /**
278
     * Return the vendor path
279
     * @return string
280
     */
281
    public function getVendorPath(): string
282
    {
283
        return $this->vendorPath;
284
    }
285
286
    /**
287
     * Set vendor path
288
     * @param string $vendorPath
289
     * @return $this
290
     */
291
    public function setVendorPath(string $vendorPath): self
292
    {
293
        $this->vendorPath = $vendorPath;
294
295
        return $this;
296
    }
297
298
     /**
299
     * Return the application root path
300
     * @return string
301
     */
302
    public function getAppPath(): string
303
    {
304
        return $this->appPath;
305
    }
306
307
    /**
308
     * Set Application path
309
     * @param string $appPath
310
     * @return $this
311
     */
312
    public function setAppPath(string $appPath): self
313
    {
314
        $this->appPath = $appPath;
315
316
        return $this;
317
    }
318
319
    /**
320
     * Return the application base path
321
     * @return string
322
     */
323
    public function getBasePath(): string
324
    {
325
        return $this->basePath;
326
    }
327
328
    /**
329
     * Set the application base path
330
     * @param string $basePath
331
     * @return $this
332
     */
333
    public function setBasePath(string $basePath): self
334
    {
335
        $this->basePath = $basePath;
336
337
        return $this;
338
    }
339
340
    /**
341
     * Return the application configuration path
342
     * @return string
343
     */
344
    public function getConfigPath(): string
345
    {
346
        return $this->configPath;
347
    }
348
349
    /**
350
     * Set the application configuration path
351
     * @param string $configPath
352
     * @return $this
353
     */
354
    public function setConfigPath(string $configPath): self
355
    {
356
        $this->configPath = $configPath;
357
358
        return $this;
359
    }
360
361
    /**
362
     * Dispatches an event to all registered listeners.
363
     * @param  string|EventInterface $eventName the name of event
364
     * of instance of EventInterface
365
     * @param  EventInterface|null $event  the instance of EventInterface or null
366
     * @return $this
367
     */
368
    public function dispatch($eventName, EventInterface $event = null): self
369
    {
370
        $this->dispatcher->dispatch($eventName, $event);
371
372
        return $this;
373
    }
374
375
    /**
376
     * Register a listener for the given event.
377
     *
378
     * @param string $eventName the name of event
379
     * @param ListenerInterface|callable|string $listener the Listener
380
     * interface or any callable
381
     * @param int $priority the listener execution priority
382
     * @return $this
383
     */
384
    public function listen(
385
        string $eventName,
386
        $listener,
387
        int $priority = DispatcherInterface::PRIORITY_DEFAULT
388
    ): self {
389
        if (is_string($listener)) {
390
            $listener = $this->createListener($listener);
391
        }
392
        $this->dispatcher->addListener($eventName, $listener, $priority);
393
394
        return $this;
395
    }
396
397
    /**
398
     * Add event subscriber
399
     * @param SubscriberInterface $subscriber
400
     * @return $this
401
     */
402
    public function subscribe(SubscriberInterface $subscriber): self
403
    {
404
        $this->dispatcher->addSubscriber($subscriber);
405
406
        return $this;
407
    }
408
409
    /**
410
     * Return the list of providers
411
     * @return array<string, ServiceProvider>
412
     */
413
    public function getProviders(): array
414
    {
415
        return $this->providers;
416
    }
417
418
    /**
419
     * Return the list of service providers commands
420
     * @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...
421
     */
422
    public function getProvidersCommands(): array
423
    {
424
        $commands = [];
425
        foreach ($this->providers as /** @var ServiceProvider $provider */ $provider) {
426
            $commands = array_merge($commands, $provider->getCommands());
427
        }
428
429
        return $commands;
430
    }
431
432
    /**
433
     * Boot the application
434
     * @return void
435
     */
436
    public function boot(): void
437
    {
438
        if ($this->booted) {
439
            return;
440
        }
441
442
        foreach ($this->providers as $provider) {
443
            $this->bootServiceProvider($provider);
444
        }
445
446
        $this->booted = true;
447
    }
448
449
    /**
450
     * Register the service provider
451
     * @param string|ServiceProvider $provider
452
     * @param bool $force whether to force registration of provider
453
     * if already loaded
454
     * @return ServiceProvider
455
     */
456
    public function registerServiceProvider(
457
        $provider,
458
        bool $force = false
459
    ): ServiceProvider {
460
        $registered = $this->getServiceProvider($provider);
461
        if ($registered && !$force) {
462
            return $registered;
463
        }
464
465
        if (is_string($provider)) {
466
            $provider = $this->createServiceProvider($provider);
467
        }
468
469
        $provider->register();
470
471
        $this->markProviderAsRegistered($provider);
472
473
        if ($this->booted) {
474
            $this->bootServiceProvider($provider);
475
        }
476
477
        return $provider;
478
    }
479
480
    /**
481
     * Return the registered service provider if exist
482
     * @param string|ServiceProvider $provider
483
     * @return ServiceProvider|null
484
     */
485
    public function getServiceProvider($provider): ?ServiceProvider
486
    {
487
        $name = is_string($provider)
488
                ? $provider
489
                : get_class($provider);
490
491
        return $this->providers[$name] ?? null;
492
    }
493
494
    /**
495
     * Load configured service providers
496
     * @return void
497
     */
498
    public function registerConfiguredServiceProviders(): void
499
    {
500
        /** @template T @var Config<T> $config */
501
        $config = $this->get(Config::class);
502
503
        /** @var string[] $providers */
504
        $providers = $config->get('providers', []);
505
        foreach ($providers as $provider) {
506
            $this->registerServiceProvider($provider);
507
        }
508
    }
509
510
    /**
511
     * Load configured events and listeners
512
     * @return void
513
     */
514
    public function registerConfiguredEvents(): void
515
    {
516
        /** @template T @var Config<T> $config */
517
        $config = $this->get(Config::class);
518
519
        /** @var array<string, string[]> $events */
520
        $events = $config->get('events', []);
521
        foreach ($events as $eventName => $listeners) {
522
            foreach ($listeners as $listener) {
523
                $this->listen($eventName, $listener);
524
            }
525
        }
526
    }
527
528
    /**
529
     * Load the application configuration
530
     * @return void
531
     */
532
    public function registerConfiguration(): void
533
    {
534
        $loader = new FileLoader($this->getConfigPath());
535
        $config = new Config($loader, $this->env);
536
        $this->instance($loader);
537
        $this->instance($config);
538
539
        date_default_timezone_set($config->get('app.timezone', 'UTC'));
540
    }
541
542
    /**
543
     * Load the environment variables if the file exists
544
     * @return void
545
     */
546
    public function registerEnvironmentVariables(): void
547
    {
548
        if (!empty($this->rootPath)) {
549
            $path = Path::normalizePath($this->rootPath);
550
            $file = rtrim($path, DIRECTORY_SEPARATOR)
551
                    . DIRECTORY_SEPARATOR . $this->environmentFile;
552
553
            if (is_file($file)) {
554
                (new Loader())
555
                    ->load(
556
                        $file,
557
                        false,
558
                        Loader::ENV | Loader::PUTENV
559
                    );
560
            }
561
        }
562
    }
563
564
    /**
565
     * Create service provider
566
     * @param string $provider
567
     * @return ServiceProvider
568
     */
569
    protected function createServiceProvider(string $provider): ServiceProvider
570
    {
571
        return new $provider($this);
572
    }
573
574
    /**
575
     * Boot the given service provider
576
     * @param ServiceProvider $provider
577
     * @return void
578
     */
579
    protected function bootServiceProvider(ServiceProvider $provider): void
580
    {
581
        $provider->boot();
582
    }
583
584
    /**
585
     * Set the given service provider as registered
586
     * @param ServiceProvider $provider
587
     * @return void
588
     */
589
    protected function markProviderAsRegistered(ServiceProvider $provider): void
590
    {
591
        $this->providers[get_class($provider)] = $provider;
592
    }
593
594
    /**
595
     * Load framework core service providers
596
     * @return void
597
     */
598
    protected function loadCoreServiceProviders(): void
599
    {
600
        $this->registerServiceProvider(new BaseServiceProvider($this));
601
        $this->registerServiceProvider(new EventServiceProvider($this));
602
    }
603
604
    /**
605
     * Create listener using the container or direct class instance
606
     * @param string $listener
607
     * @return ListenerInterface
608
     */
609
    protected function createListener(string $listener): ListenerInterface
610
    {
611
        if ($this->has($listener)) {
612
            return $this->get($listener);
613
        }
614
615
        if (class_exists($listener)) {
616
            return new $listener();
617
        }
618
619
        throw new InvalidArgumentException(sprintf(
620
            'Can not resolve the listener class [%s], check if this is the'
621
                . ' identifier of container or class exists',
622
            $listener
623
        ));
624
    }
625
}
626