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