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