Passed
Push — master ( 7ae5cf...c94021 )
by Melech
01:27
created

Valkyrja::addComponentCliControllers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 6
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Application;
15
16
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Valkyrja\Application\Contract\Application;
0 ignored issues
show
Bug introduced by
The type Valkyrja\Application\Contract\Application was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Valkyrja\Application\Exception\RuntimeException;
19
use Valkyrja\Application\Support\Component;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Valkyrja\Application\Component. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
20
use Valkyrja\Cli\Routing\Data as CliData;
21
use Valkyrja\Container\Contract\Container;
22
use Valkyrja\Container\Data as ContainerData;
23
use Valkyrja\Event\Data as EventData;
24
use Valkyrja\Http\Routing\Data as HttpData;
25
26
/**
27
 * Class Valkyrja.
28
 *
29
 * @author Melech Mizrachi
30
 */
31
class Valkyrja implements Application
32
{
33
    /**
34
     * Application env.
35
     */
36
    protected Env $env;
0 ignored issues
show
Bug introduced by
The type Valkyrja\Application\Env was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
38
    /**
39
     * Application config.
40
     */
41
    protected Config|null $config = null;
42
43
    /**
44
     * Application data.
45
     */
46
    protected Data|null $data = null;
47
48
    /**
49
     * Get the instance of the container.
50
     */
51
    protected Container $container;
52
53
    /**
54
     * Whether the application was setup.
55
     */
56
    protected bool $setup = false;
57
58
    /**
59
     * Application constructor.
60
     */
61
    public function __construct(Env $env, Config|Data $configData = new Config())
62
    {
63
        $this->setup(env: $env, configData: $configData);
64
    }
65
66
    /**
67
     * @inheritDoc
68
     */
69
    #[Override]
70
    public function setup(Env $env, Config|Data $configData = new Config(), bool $force = false): void
71
    {
72
        // If the application was already setup, no need to do it again
73
        if ($this->setup && ! $force) {
74
            return;
75
        }
76
77
        // Avoid re-setting up the app later
78
        $this->setup = true;
79
80
        $this->setEnv(env: $env);
81
82
        $this->bootstrapContainer();
83
84
        if ($configData instanceof Config) {
85
            $this->bootstrapConfig(config: $configData);
86
        } else {
87
            $this->bootstrapData(data: $configData);
88
        }
89
90
        $this->bootstrapServices();
91
    }
92
93
    /**
94
     * @inheritDoc
95
     */
96
    #[Override]
97
    public function addComponent(string $component): void
98
    {
99
        if ($this->config === null) {
100
            throw new RuntimeException('Cannot add components to an app setup with Data');
101
        }
102
103
        $config = $this->config;
104
105
        $this->addComponentContainerAliases($config, $component);
106
        $this->addComponentContainerServices($config, $component);
107
        $this->addComponentContainerProviders($component);
108
        $this->addComponentEventListeners($config, $component);
109
        $this->addComponentCliControllers($config, $component);
110
        $this->addComponentHttpControllers($config, $component);
111
    }
112
113
    /**
114
     * @inheritDoc
115
     *
116
     * @return Env
117
     */
118
    #[Override]
119
    public function getEnv(): Env
120
    {
121
        return $this->env;
122
    }
123
124
    /**
125
     * @inheritDoc
126
     */
127
    #[Override]
128
    public function setEnv(Env $env): void
129
    {
130
        // Set the env class to use
131
        $this->env = $env;
132
133
        $this->bootstrapTimezone();
134
    }
135
136
    /**
137
     * @inheritDoc
138
     */
139
    #[Override]
140
    public function getContainer(): Container
141
    {
142
        return $this->container;
143
    }
144
145
    /**
146
     * @inheritDoc
147
     */
148
    #[Override]
149
    public function setContainer(Container $container): static
150
    {
151
        $this->container = $container;
152
153
        return $this;
154
    }
155
156
    /**
157
     * @inheritDoc
158
     */
159
    #[Override]
160
    public function getDebugMode(): bool
161
    {
162
        /** @var bool $debugMode */
163
        $debugMode = $this->env::APP_DEBUG_MODE;
164
165
        return $debugMode;
166
    }
167
168
    /**
169
     * @inheritDoc
170
     */
171
    #[Override]
172
    public function getEnvironment(): string
173
    {
174
        /** @var non-empty-string $env */
175
        $env = $this->env::APP_ENV;
176
177
        return $env;
178
    }
179
180
    /**
181
     * @inheritDoc
182
     */
183
    #[Override]
184
    public function getVersion(): string
185
    {
186
        /** @var non-empty-string $version */
187
        $version = $this->env::APP_VERSION;
188
189
        return $version;
190
    }
191
192
    /**
193
     * Bootstrap the config.
194
     */
195
    protected function bootstrapConfig(Config $config): void
196
    {
197
        $this->config = $config;
198
199
        $this->bootstrapComponents();
200
    }
201
202
    /**
203
     * Bootstrap all the components types for the application.
204
     */
205
    protected function bootstrapComponents(): void
206
    {
207
        $this->bootstrapRequiredComponents();
208
        $this->bootstrapCoreComponents();
209
        $this->bootstrapOptionalComponents();
210
    }
211
212
    /**
213
     * Bootstrap all the required components for the application to run.
214
     */
215
    protected function bootstrapRequiredComponents(): void
216
    {
217
        /** @var class-string<Component>[] $components */
218
        $components = $this->env::APP_REQUIRED_COMPONENTS;
219
220
        foreach ($components as $component) {
221
            $this->addComponent(component: $component);
222
        }
223
    }
224
225
    /**
226
     * Bootstrap all the core components to run specific parts of the application.
227
     */
228
    protected function bootstrapCoreComponents(): void
229
    {
230
        /** @var class-string<Component>[] $components */
231
        $components = $this->env::APP_CORE_COMPONENTS;
232
233
        foreach ($components as $component) {
234
            $this->addComponent(component: $component);
235
        }
236
    }
237
238
    /**
239
     * Bootstrap all the optional components.
240
     */
241
    protected function bootstrapOptionalComponents(): void
242
    {
243
        /** @var class-string<Component>[] $components */
244
        $components = $this->env::APP_COMPONENTS;
245
246
        foreach ($components as $component) {
247
            $this->addComponent(component: $component);
248
        }
249
    }
250
251
    /**
252
     * Add a component's container aliases.
253
     *
254
     * @param class-string<Component> $component The component class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Component> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Component>.
Loading history...
255
     */
256
    protected function addComponentContainerAliases(Config $config, string $component): void
257
    {
258
        $config->aliases = [
259
            ...$config->aliases,
260
            ...$component::getContainerAliases(),
261
        ];
262
    }
263
264
    /**
265
     * Add a component's container services.
266
     *
267
     * @param class-string<Component> $component The component class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Component> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Component>.
Loading history...
268
     */
269
    protected function addComponentContainerServices(Config $config, string $component): void
270
    {
271
        $config->services = [
272
            ...$config->services,
273
            ...$component::getContainerServices(),
274
        ];
275
    }
276
277
    /**
278
     * Add a component's container services.
279
     *
280
     * @param class-string<Component> $component The component class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Component> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Component>.
Loading history...
281
     */
282
    protected function addComponentContainerProviders(string $component): void
283
    {
284
        array_map(
285
            [$this->container, 'register'],
286
            $component::getContainerProviders(),
287
        );
288
    }
289
290
    /**
291
     * Add a component's event listeners.
292
     *
293
     * @param class-string<Component> $component The component class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Component> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Component>.
Loading history...
294
     */
295
    protected function addComponentEventListeners(Config $config, string $component): void
296
    {
297
        if ($this->env::APP_ADD_EVENT_LISTENERS) {
298
            $config->listeners = [
299
                ...$config->listeners,
300
                ...$component::getEventListeners(),
301
            ];
302
        }
303
    }
304
305
    /**
306
     * Add a component's cli controllers.
307
     *
308
     * @param class-string<Component> $component The component class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Component> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Component>.
Loading history...
309
     */
310
    protected function addComponentCliControllers(Config $config, string $component): void
311
    {
312
        if ($this->env::APP_ADD_CLI_CONTROLLERS) {
313
            $config->commands = [
314
                ...$config->commands,
315
                ...$component::getCliControllers(),
316
            ];
317
        }
318
    }
319
320
    /**
321
     * Add a component's http controllers.
322
     *
323
     * @param class-string<Component> $component The component class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Component> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Component>.
Loading history...
324
     */
325
    protected function addComponentHttpControllers(Config $config, string $component): void
326
    {
327
        if ($this->env::APP_ADD_HTTP_CONTROLLERS) {
328
            $config->controllers = [
329
                ...$config->controllers,
330
                ...$component::getHttpControllers(),
331
            ];
332
        }
333
    }
334
335
    /**
336
     * Bootstrap the data.
337
     */
338
    protected function bootstrapData(Data $data): void
339
    {
340
        $this->data = $data;
341
    }
342
343
    /**
344
     * Create the container.
345
     */
346
    protected function bootstrapContainer(): void
347
    {
348
        $container = new \Valkyrja\Container\Container();
349
350
        $this->setContainer($container);
351
    }
352
353
    /**
354
     * Bootstrap container services.
355
     */
356
    protected function bootstrapServices(): void
357
    {
358
        $container = $this->container;
359
360
        $container->setSingleton(Application::class, $this);
361
        $container->setSingleton(Env::class, $this->env);
362
        $container->setSingleton(Container::class, $container);
363
364
        if ($this->data !== null) {
365
            $container->setSingleton(ContainerData::class, $this->data->container);
366
            $container->setSingleton(EventData::class, $this->data->event);
367
            $container->setSingleton(CliData::class, $this->data->cli);
368
            $container->setSingleton(HttpData::class, $this->data->http);
369
370
            $container->setFromData($this->data->container);
371
        }
372
373
        if ($this->config !== null) {
374
            $container->setSingleton(Config::class, $this->config);
375
376
            $data = $container->getSingleton(ContainerData::class);
377
            $container->setFromData($data);
378
        }
379
    }
380
381
    /**
382
     * Bootstrap the timezone.
383
     */
384
    protected function bootstrapTimezone(): void
385
    {
386
        /** @var non-empty-string $timezone */
387
        $timezone = $this->env::APP_TIMEZONE;
388
389
        date_default_timezone_set($timezone);
390
    }
391
}
392