Config::getAppRootDir()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework\Config;
6
7
use Gacela\Framework\Bootstrap\SetupGacelaInterface;
8
use Gacela\Framework\Event\Dispatcher\EventDispatcherInterface;
9
use Gacela\Framework\Exception\ConfigException;
10
use Override;
11
use RuntimeException;
12
13
use function array_key_exists;
14
15
final class Config implements ConfigInterface
16
{
17
    private static ?self $instance = null;
18
19
    private static ?EventDispatcherInterface $eventDispatcher = null;
20
21
    private ?ConfigFactory $configFactory = null;
22
23
    private ?string $appRootDir = null;
24
25
    /** @var array<string,mixed> */
26
    private array $config = [];
27 106
28
    private ?string $cacheDir = null;
29
30 106
    private function __construct(
31
        private readonly SetupGacelaInterface $setup,
32 106
    ) {
33
    }
34 106
35
    public static function createWithSetup(SetupGacelaInterface $setup): self
36 106
    {
37
        self::$instance = new self($setup);
38
39 89
        return self::$instance;
40
    }
41 89
42
    public static function getInstance(): self
43
    {
44
        if (!self::$instance instanceof self) {
45 89
            throw new RuntimeException('You have to call createWithSetup() first. Have you forgot to bootstrap Gacela?');
46
        }
47
48
        return self::$instance;
49
    }
50
51 58
    /**
52
     * @internal
53 58
     */
54 58
    public static function resetInstance(): void
55
    {
56
        self::$instance = null;
57 89
        self::$eventDispatcher = null;
58
    }
59 89
60 55
    public static function getEventDispatcher(): EventDispatcherInterface
61 55
    {
62 55
        if (!self::$eventDispatcher instanceof EventDispatcherInterface) {
63
            self::$eventDispatcher = self::getInstance()
64
                ->getSetupGacela()
65 89
                ->getEventDispatcher();
66
        }
67
68
        return self::$eventDispatcher;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::eventDispatcher could return the type null which is incompatible with the type-hinted return Gacela\Framework\Event\D...ventDispatcherInterface. Consider adding an additional type-check to rule them out.
Loading history...
69
    }
70
71 24
    /**
72
     * @throws ConfigException
73 24
     */
74 3
    #[Override]
75
    public function get(string $key, mixed $default = self::DEFAULT_CONFIG_VALUE): mixed
76
    {
77 24
        if ($this->config === []) {
78 2
            $this->init();
79
        }
80
81 22
        if ($default !== self::DEFAULT_CONFIG_VALUE && !$this->hasKey($key)) {
82 1
            return $default;
83
        }
84
85 21
        if (!$this->hasKey($key)) {
86
            throw ConfigException::keyNotFound($key, self::class);
87
        }
88
89
        return $this->config[$key];
90
    }
91
92
    /**
93 106
     * Force loading all config values in memory.
94
     *
95 106
     * @throws ConfigException
96 106
     */
97 106
    public function init(): void
98
    {
99
        $this->configFactory = null;
100 106
101
        /** @psalm-suppress DuplicateArrayKey */
102 106
        $this->config = [
103
            ...$this->loadAllConfigValues(),
104 106
            ...$this->setup->getConfigKeyValues(),
105
        ];
106
    }
107
108 106
    public function setAppRootDir(string $dir): self
109
    {
110
        $this->appRootDir = rtrim($dir, DIRECTORY_SEPARATOR);
111 106
112
        if ($this->appRootDir === '' || $this->appRootDir === '0') {
113 106
            $this->appRootDir = getcwd() ?: ''; // @codeCoverageIgnore
114
        }
115
116 5
        return $this;
117
    }
118 5
119 5
    public function getAppRootDir(): string
120 5
    {
121
        return $this->appRootDir ?? getcwd() ?: '';
122
    }
123
124
    public function getCacheDir(): string
125
    {
126 106
        if ($this->cacheDir !== null) {
127
            return $this->cacheDir;
128 106
        }
129 106
130 106
        $this->cacheDir = getenv('GACELA_CACHE_DIR') ?: $this->getDefaultCacheDir();
131 106
132 106
        return rtrim($this->cacheDir, DIRECTORY_SEPARATOR);
133
    }
134
135 106
    /**
136
     * @internal
137
     */
138 106
    public function getFactory(): ConfigFactory
139
    {
140 106
        if (!$this->configFactory instanceof ConfigFactory) {
141
            $this->configFactory = new ConfigFactory(
142
                $this->getAppRootDir(),
143 57
                $this->setup,
144
            );
145 57
        }
146
147
        return $this->configFactory;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->configFactory could return the type null which is incompatible with the type-hinted return Gacela\Framework\Config\ConfigFactory. Consider adding an additional type-check to rule them out.
Loading history...
148
    }
149
150
    #[Override]
151 106
    public function getSetupGacela(): SetupGacelaInterface
152
    {
153 106
        return $this->setup;
154 106
    }
155 106
156
    #[Override]
157
    public function hasKey(string $key): bool
158
    {
159
        return array_key_exists($key, $this->config);
160
    }
161
162
    private function getDefaultCacheDir(): string
163
    {
164
        $cacheDir = $this->setup->getFileCacheDirectory();
165
        if ($cacheDir === '') {
166
            return sys_get_temp_dir();
167
        }
168
169
        $appRoot = $this->getAppRootDir();
170
171
        if (preg_match('#^[A-Za-z]:[\\/]#', $cacheDir) === 1) {
172
            return $cacheDir;
173
        }
174
175
        if ($cacheDir[0] === DIRECTORY_SEPARATOR) {
176
            if (str_starts_with($cacheDir, $appRoot . DIRECTORY_SEPARATOR)) {
177
                return $cacheDir;
178
            }
179
180
            return $appRoot . $cacheDir;
181
        }
182
183
        return $appRoot
184
            . DIRECTORY_SEPARATOR
185
            . ltrim($cacheDir, DIRECTORY_SEPARATOR);
186
    }
187
188
    /**
189
     * @return array<string,mixed>
190
     */
191
    private function loadAllConfigValues(): array
192
    {
193
        return $this->getFactory()
194
            ->createConfigLoader()
195
            ->loadAll();
196
    }
197
}
198