Config   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Test Coverage

Coverage 98.18%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 32
eloc 61
c 4
b 0
f 0
dl 0
loc 178
ccs 54
cts 55
cp 0.9818
rs 9.84

15 Methods

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