Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
72.00% covered (warning)
72.00%
18 / 25
66.67% covered (warning)
66.67%
6 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Config
72.00% covered (warning)
72.00%
18 / 25
66.67% covered (warning)
66.67%
6 / 9
16.71
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 requireEnv
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 with
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 has
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get
28.57% covered (danger)
28.57%
2 / 7
0.00% covered (danger)
0.00%
0 / 1
6.28
 debug
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 env
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 normalizeRoot
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 printAll
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray;
6
7use Celemas\Core\Exception\OutOfBoundsException;
8use Celemas\Core\Exception\ValueError;
9
10/**
11 * @psalm-import-type BuiltinConfig from \Cosray\Config\Types
12 * @psalm-import-type BuiltinConfigInput from \Cosray\Config\Types
13 */
14class Config
15{
16    /** @var BuiltinConfig&array<string, mixed> */
17    private readonly array $settings;
18
19    private readonly string $root;
20    private readonly Config\Env $environment;
21
22    private ?Config\App $appConfig = null;
23    private ?Config\Auth $authConfig = null;
24    private ?Config\Path $pathConfig = null;
25    private ?Config\Panel $panelConfig = null;
26    private ?Config\Error $errorConfig = null;
27    private ?Config\Icons $iconsConfig = null;
28    private ?Config\Database $dbConfig = null;
29    private ?Config\Session $sessionConfig = null;
30    private ?Config\Media $mediaConfig = null;
31    private ?Config\Upload $uploadConfig = null;
32    private ?Config\Password $passwordConfig = null;
33    private ?Config\Uid $uidConfig = null;
34
35    public Config\App $app {
36        get => $this->appConfig ??= new Config\App($this);
37    }
38
39    public Config\Auth $auth {
40        get => $this->authConfig ??= new Config\Auth($this);
41    }
42
43    public Config\Path $path {
44        get => $this->pathConfig ??= new Config\Path($this);
45    }
46
47    public Config\Panel $panel {
48        get => $this->panelConfig ??= new Config\Panel($this);
49    }
50
51    public Config\Error $error {
52        get => $this->errorConfig ??= new Config\Error($this);
53    }
54
55    public Config\Icons $icons {
56        get => $this->iconsConfig ??= new Config\Icons($this);
57    }
58
59    public Config\Database $db {
60        get => $this->dbConfig ??= new Config\Database($this);
61    }
62
63    public Config\Session $session {
64        get => $this->sessionConfig ??= new Config\Session($this);
65    }
66
67    public Config\Media $media {
68        get => $this->mediaConfig ??= new Config\Media($this);
69    }
70
71    public Config\Upload $upload {
72        get => $this->uploadConfig ??= new Config\Upload($this);
73    }
74
75    public Config\Password $password {
76        get => $this->passwordConfig ??= new Config\Password($this);
77    }
78
79    public Config\Uid $uid {
80        get => $this->uidConfig ??= new Config\Uid($this);
81    }
82
83    /** @param BuiltinConfigInput&array<string, mixed> $settings */
84    public function __construct(string $root, array $settings = [])
85    {
86        $this->root = $this->normalizeRoot($root);
87        $this->environment = Config\Env::load($this->root);
88        $this->environment->validate();
89        $this->settings = Config\Settings::merge(
90            Config\Defaults::values($this->root, $this->environment),
91            $settings,
92        );
93    }
94
95    /** @param non-empty-string|list<non-empty-string> $variables */
96    public function requireEnv(string|array $variables): self
97    {
98        $this->environment->require($variables);
99
100        return $this;
101    }
102
103    /**
104     * @template TKey of string
105     * @param TKey $key
106     * @param (TKey is key-of<BuiltinConfig> ? BuiltinConfig[TKey] : mixed) $value
107     */
108    public function with(string $key, mixed $value): self
109    {
110        $settings = Config\Settings::merge($this->settings, [$key => $value]);
111
112        return new self($this->root, $settings);
113    }
114
115    public function has(string $key): bool
116    {
117        return array_key_exists($key, $this->settings);
118    }
119
120    /**
121     * @template TKey of string
122     * @param TKey $key
123     * @return (TKey is key-of<BuiltinConfig> ? BuiltinConfig[TKey] : mixed)
124     */
125    public function get(string $key, mixed $default = null): mixed
126    {
127        if (array_key_exists($key, $this->settings)) {
128            return $this->settings[$key];
129        }
130
131        if (func_num_args() > 1) {
132            return $default;
133        }
134
135        throw new OutOfBoundsException(
136            "The configuration key '{$key}' does not exist",
137        );
138    }
139
140    public function debug(): bool
141    {
142        return $this->app->debug;
143    }
144
145    public function env(): string
146    {
147        return $this->app->env;
148    }
149
150    protected function normalizeRoot(string $root): string
151    {
152        if ($root === '') {
153            throw new ValueError('The root path must be a non-empty string.');
154        }
155
156        return rtrim($root, '/\\') ?: DIRECTORY_SEPARATOR;
157    }
158
159    public function printAll(): void
160    {
161        error_log(print_r($this->settings, true));
162    }
163}