Test Failed
Push — develop ( 8b5bdd...c753fd )
by nguereza
02:34
created

Application::subscribe()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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