Passed
Push — develop ( 69cbbf...c95d07 )
by nguereza
02:37
created

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