Passed
Push — master ( 18ca7f...000a22 )
by Filipe
02:02 queued 14s
created

AbstractApplication::container()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * This file is part of web-stack
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Slick\WebStack\Infrastructure;
13
14
use Composer\Autoload\ClassLoader;
15
use Dotenv\Dotenv;
16
use Slick\Configuration\ConfigurationInterface;
17
use Slick\Configuration\Driver\Environment;
18
use Slick\Configuration\PriorityConfigurationChain;
19
use Slick\Di\ContainerInterface;
20
use Slick\ModuleApi\Infrastructure\SlickModuleInterface;
21
use function Slick\ModuleApi\importSettingsFile;
22
use function Slick\ModuleApi\mergeArrays;
23
24
/**
25
 * AbstractApplication
26
 *
27
 * @package Slick\WebStack\Infrastructure
28
 */
29
abstract class AbstractApplication
30
{
31
32
    public const MODULES_PATH = "/config/modules/enabled.php";
33
34
    protected DependencyContainerFactory $containerFactory;
35
36
    /** @var array<SlickModuleInterface>  */
37
    protected array $modules = [];
38
39
    protected Dotenv $dotenv;
40
41
    private ?ContainerInterface $container = null;
42
43
    /**
44
     * Creates an AbstractApplication
45
     *
46
     * @param string $rootPath
47
     * @param ClassLoader|null $classLoader
48
     */
49
    public function __construct(
50
        protected readonly string $rootPath,
51
        protected readonly ?ClassLoader $classLoader = null
52
    ) {
53
        if (!defined('APP_ROOT')) {
54
            define("APP_ROOT", $this->rootPath);
55
        }
56
        $this->dotenv = Dotenv::createImmutable($this->rootPath);
57
        $this->containerFactory = DependencyContainerFactory::instance();
58
    }
59
60
    public function container(): ContainerInterface
61
    {
62
        return $this->prepareContainer();
63
    }
64
65
    /**
66
     * Retrieves the root path of the application.
67
     *
68
     * @return string The root path.
69
     */
70
    public function rootPath(): string
71
    {
72
        return $this->rootPath;
73
    }
74
75
    /**
76
     * Run the application and return the result.
77
     *
78
     * @return mixed The result of running the code.
79
     */
80
    abstract public function run(): mixed;
81
82
    /**
83
     * Prepare the container by loading modules, settings, and services.
84
     *
85
     * @return ContainerInterface The prepared container.
86
     */
87
    protected function prepareContainer(): ContainerInterface
88
    {
89
        if ($this->container) {
90
            return $this->container;
91
        }
92
93
        $this->loadModules();
94
        $settingsDriver = new PriorityConfigurationChain();
95
        $settingsDriver->add(new Environment(), 10);
96
        $settingsDriver->add(new ArrayConfigurationDriver($this->loadSettings()), 30);
97
        $this->loadServices();
98
        $container = $this->containerFactory->container();
99
        $container->register('settings', $settingsDriver);
100
        $container->register(ConfigurationInterface::class, '@settings');
101
        $container->register('app.root', $this->rootPath());
102
103
        if ($this->classLoader) {
104
            $container->register(ClassLoader::class, $this->classLoader);
105
        }
106
107
        $this->container = $container;
108
        return $container;
109
    }
110
111
    /**
112
     * Load services from all modules and register them in the container.
113
     *
114
     * @return void
115
     */
116
    protected function loadServices(): void
117
    {
118
        $services = [];
119
        foreach ($this->modules as $module) {
120
            $services = array_merge($services, $module->services());
121
        }
122
123
        $this->containerFactory->loadApplicationServices($this->rootPath(), $services);
124
    }
125
126
    /**
127
     * Loads the settings from the modules and the external file
128
     *
129
     * @return array<string, mixed> The loaded settings
130
     */
131
    protected function loadSettings(): array
132
    {
133
        $settings = [];
134
        $this->dotenv->safeLoad();
135
        foreach ($this->modules as $module) {
136
            $moduleSettings = $module->settings($this->dotenv);
137
            $settings = mergeArrays($moduleSettings, $settings);
138
        }
139
        $importSettingsFile = importSettingsFile(APP_ROOT . '/config/modules.php');
140
        return mergeArrays($settings, $importSettingsFile);
141
    }
142
143
    /**
144
     * Load modules into the application.
145
     */
146
    public function loadModules(): void
147
    {
148
        $file = $this->rootPath . self::MODULES_PATH;
149
        if (!is_file($file)) {
150
            return;
151
        }
152
153
        $modules = require $file;
154
        foreach ($modules as $module) {
155
            $loadedModule = new $module();
156
            if ($loadedModule instanceof SlickModuleInterface) {
157
                $this->modules[] = $loadedModule;
158
            }
159
        }
160
    }
161
}
162