Passed
Push — develop ( 43a598...04e7b2 )
by nguereza
02:46
created

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