Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
13 / 13
CRAP
100.00% covered (success)
100.00%
1 / 1
Config
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
13 / 13
25
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 setPersistence
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 replacePersistence
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getPersistentConfigs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 set
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 getInstances
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 setMany
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getAll
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDir
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getDir
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 load
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Config Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Config;
11
12use Framework\Helpers\Isolation;
13use LogicException;
14use SensitiveParameter;
15
16/**
17 * Class Config.
18 *
19 * @package config
20 */
21class Config
22{
23    /**
24     * @var array<string,array<string,array<mixed>>>
25     */
26    protected array $configs = [];
27    protected ?string $configsDir = null;
28    /**
29     * @var array<string,array<string,array<mixed>>>
30     */
31    protected array $persistence = [];
32    protected string $suffix;
33
34    /**
35     * Config constructor.
36     *
37     * @param array<string,array<string,array<mixed>>>|string|null $configs An
38     * array to set many configs, the config base directory or null
39     * @param array<string,array<string,array<mixed>>> $persistence Configs that
40     * will always overwrite custom added, loaded or set configs
41     * @param string $suffix The services filenames suffix used when the config
42     * directory is set
43     */
44    public function __construct(
45        #[SensitiveParameter] array | string $configs = null,
46        #[SensitiveParameter] array $persistence = [],
47        string $suffix = '.php'
48    ) {
49        if ($configs !== null) {
50            \is_array($configs)
51                ? $this->setMany($configs)
52                : $this->setDir($configs);
53        }
54        $this->setPersistence($persistence);
55        $this->suffix = $suffix;
56    }
57
58    /**
59     * Set persistent configs.
60     *
61     * @param array<string,array<mixed>> $configs
62     */
63    protected function setPersistence(#[SensitiveParameter] array $configs) : void
64    {
65        $this->persistence = $configs;
66    }
67
68    /**
69     * Replace configs with persistent configs.
70     */
71    protected function replacePersistence() : void
72    {
73        if (empty($this->persistence)) {
74            return;
75        }
76        $this->configs = \array_replace_recursive($this->configs, $this->persistence);
77    }
78
79    /**
80     * Get configs with persistence.
81     *
82     * @param string $name The service name
83     * @param string $instance The service instance
84     *
85     * @return array<mixed> The service instance custom configs with
86     * persistent configs
87     */
88    protected function getPersistentConfigs(string $name, string $instance) : array
89    {
90        $this->replacePersistence();
91        return $this->configs[$name][$instance] ?? [];
92    }
93
94    /**
95     * Set configs to a service instance.
96     *
97     * NOTE: These configs will replace an existing instance (except persistence).
98     *
99     * @param string $name The service name
100     * @param array<mixed> $configs The new configs
101     * @param string $instance The service instance
102     *
103     * @return array<mixed> The service instance configs
104     */
105    public function set(
106        string $name,
107        #[SensitiveParameter] array $configs,
108        string $instance = 'default'
109    ) : array {
110        $this->configs[$name][$instance] = $configs;
111        return $this->getPersistentConfigs($name, $instance);
112    }
113
114    /**
115     * Get configs by a service instance.
116     *
117     * @param string $name The service name
118     * @param string $instance The service instance
119     *
120     * @throws LogicException If the service configs are empty and the Config
121     * directory is set, and the config file is not found
122     *
123     * @return array<mixed>|null The instance configs as array or null
124     * if is not set
125     */
126    public function get(string $name, string $instance = 'default') : ?array
127    {
128        if (empty($this->configs[$name]) && isset($this->configsDir)) {
129            $this->load($name);
130        }
131        return $this->configs[$name][$instance] ?? null;
132    }
133
134    /**
135     * Get service instances configs.
136     *
137     * @param string $name The service name
138     *
139     * @return array<string,array<string,mixed>>|null The service instance names as
140     * keys and its configs as values or null if the service is not set
141     */
142    public function getInstances(string $name) : ?array
143    {
144        return $this->configs[$name] ?? null;
145    }
146
147    /**
148     * Add configs to a service instance.
149     *
150     * NOTE: IF the service instance already exists, the configs will be merged
151     *
152     * @param string $name The service name
153     * @param array<mixed> $configs The service configs
154     * @param string $instance The service instance
155     *
156     * @return array<mixed> The service instance configs
157     */
158    public function add(
159        string $name,
160        #[SensitiveParameter] array $configs,
161        string $instance = 'default'
162    ) : array {
163        if (isset($this->configs[$name][$instance])) {
164            $this->configs[$name][$instance] = \array_replace_recursive(
165                $this->configs[$name][$instance],
166                $configs
167            );
168            return $this->getPersistentConfigs($name, $instance);
169        }
170        return $this->set($name, $configs, $instance);
171    }
172
173    /**
174     * Set many configs in one call.
175     *
176     * NOTE: The $configs will replace existing instances (except persistence).
177     *
178     * @param array<string,array<string,array<mixed>>> $configs The service
179     * names as keys and its instance configs as values
180     *
181     * @return static
182     */
183    public function setMany(#[SensitiveParameter] array $configs) : static
184    {
185        foreach ($configs as $name => $values) {
186            foreach ($values as $instance => $config) {
187                $this->set($name, $config, $instance);
188            }
189        }
190        return $this;
191    }
192
193    /**
194     * Get all configs.
195     *
196     * @return array<string,array<string,array<mixed>>> All many configs
197     */
198    public function getAll() : array
199    {
200        return $this->configs;
201    }
202
203    /**
204     * Set the base directory.
205     *
206     * @param string $directory Directory path
207     *
208     * @throws LogicException If the config directory is not found
209     *
210     * @return static
211     */
212    public function setDir(string $directory) : static
213    {
214        $dir = \realpath($directory);
215        if ($dir === false || ! \is_dir($dir)) {
216            throw new LogicException('Config directory not found: ' . $directory);
217        }
218        $this->configsDir = $dir . \DIRECTORY_SEPARATOR;
219        return $this;
220    }
221
222    /**
223     * Get the base directory.
224     *
225     * @return string|null The directory realpath or null if it was not set
226     */
227    public function getDir() : ?string
228    {
229        return $this->configsDir;
230    }
231
232    /**
233     * Load a config file.
234     *
235     * @param string $name The file name without the directory path and the suffix
236     *
237     * @throws LogicException If the config file is not found
238     *
239     * @return static
240     */
241    public function load(string $name) : static
242    {
243        $filename = $this->configsDir . $name . $this->suffix;
244        $filename = \realpath($filename);
245        if ($filename === false || ! \is_file($filename)) {
246            throw new LogicException('Config file not found: ' . $name);
247        }
248        $configs = Isolation::require($filename);
249        $this->setMany([$name => $configs]);
250        return $this;
251    }
252}