3 * @copyright Copyright (C) 2010-2023, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Core\Config\ValueObject;
24 use Friendica\Core\Config\Util\ConfigFileManager;
25 use ParagonIE\HiddenString\HiddenString;
28 * The Friendica config cache for the application
29 * Initial, all *.config.php files are loaded into this cache with the
30 * ConfigFileManager ( @see ConfigFileManager )
34 /** @var int Indicates that the cache entry is a default value - Lowest Priority */
35 const SOURCE_STATIC = 0;
36 /** @var int Indicates that the cache entry is set by file - Low Priority */
37 const SOURCE_FILE = 1;
38 /** @var int Indicates that the cache entry is manually set by the application (per admin page/console) - Middle Priority */
39 const SOURCE_DATA = 2;
40 /** @var int Indicates that the cache entry is set by a server environment variable - High Priority */
42 /** @var int Indicates that the cache entry is fixed and must not be changed */
45 /** @var int Default value for a config source */
46 const SOURCE_DEFAULT = self::SOURCE_FILE;
61 private $hidePasswordOutput;
64 * @param array $config A initial config array
65 * @param bool $hidePasswordOutput True, if cache variables should take extra care of password values
66 * @param int $source Sets a source of the initial config values
68 public function __construct(array $config = [], bool $hidePasswordOutput = true, int $source = self::SOURCE_DEFAULT)
70 $this->hidePasswordOutput = $hidePasswordOutput;
71 $this->load($config, $source);
75 * Tries to load the specified configuration array into the config array.
76 * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
78 * @param array $config
79 * @param int $source Indicates the source of the config entry
81 public function load(array $config, int $source = self::SOURCE_DEFAULT)
83 $categories = array_keys($config);
85 foreach ($categories as $category) {
86 if (is_array($config[$category])) {
87 $keys = array_keys($config[$category]);
89 foreach ($keys as $key) {
90 $this->set($category, $key, $config[$category][$key] ?? null, $source);
93 $this->set($category, null, $config[$category], $source);
99 * Gets a value from the config cache.
101 * @param string $cat Config category
102 * @param string|null $key Config key
104 * @return null|mixed Returns the value of the Config entry or null if not set
106 public function get(string $cat, ?string $key = null)
108 if (isset($this->config[$cat][$key])) {
109 return $this->config[$cat][$key];
110 } elseif (!isset($key) && isset($this->config[$cat])) {
111 return $this->config[$cat];
118 * Returns the source value of the current, cached config value
120 * @param string $cat Config category
121 * @param string $key Config key
125 public function getSource(string $cat, string $key): int
127 return $this->source[$cat][$key] ?? -1;
131 * Returns the whole config array based on the given source type
133 * @param int $source Indicates the source of the config entry
135 * @return array The config array part of the given source
137 public function getDataBySource(int $source): array
141 $categories = array_keys($this->source);
143 foreach ($categories as $category) {
144 if (is_array($this->source[$category])) {
145 $keys = array_keys($this->source[$category]);
147 foreach ($keys as $key) {
148 if ($this->source[$category][$key] === $source) {
149 $data[$category][$key] = $this->config[$category][$key];
152 } elseif (is_int($this->source[$category])) {
153 $data[$category] = null;
161 * Sets a value in the config cache. Accepts raw output from the config table
163 * @param string $cat Config category
164 * @param ?string $key Config key
165 * @param ?mixed $value Value to set
166 * @param int $source The source of the current config key
168 * @return bool True, if the value is set
170 public function set(string $cat, string $key = null, $value = null, int $source = self::SOURCE_DEFAULT): bool
172 if (!isset($this->config[$cat]) && $key !== null) {
173 $this->config[$cat] = [];
174 $this->source[$cat] = [];
177 if ((isset($this->source[$cat][$key]) && $source < $this->source[$cat][$key]) ||
178 (is_int($this->source[$cat] ?? null) && $source < $this->source[$cat])) {
182 if ($this->hidePasswordOutput &&
183 $key == 'password' &&
185 $this->setCatKeyValueSource($cat, $key, new HiddenString($value), $source);
186 } else if (is_string($value)) {
187 $this->setCatKeyValueSource($cat, $key, self::toConfigValue($value), $source);
189 $this->setCatKeyValueSource($cat, $key, $value, $source);
195 private function setCatKeyValueSource(string $cat, string $key = null, $value = null, int $source = self::SOURCE_DEFAULT)
198 $this->config[$cat][$key] = $value;
199 $this->source[$cat][$key] = $source;
201 $this->config[$cat] = $value;
202 $this->source[$cat] = $source;
207 * Formats a DB value to a config value
208 * - null = The db-value isn't set
209 * - bool = The db-value is either '0' or '1'
210 * - array = The db-value is a serialized array
211 * - string = The db-value is a string
213 * Keep in mind that there aren't any numeric/integer config values in the database
215 * @param string|null $value
217 * @return null|array|string
219 public static function toConfigValue(?string $value)
221 if (!isset($value)) {
225 if (preg_match("|^a:[0-9]+:{.*}$|s", $value)) {
226 return unserialize($value);
233 * Deletes a value from the config cache.
235 * @param string $cat Config category
236 * @param ?string $key Config key (if not set, the whole category will get deleted)
237 * @param int $source The source of the current config key
239 * @return bool true, if deleted
241 public function delete(string $cat, string $key = null, int $source = self::SOURCE_DEFAULT): bool
243 if (isset($this->config[$cat][$key])) {
244 $this->config[$cat][$key] = null;
245 $this->source[$cat][$key] = $source;
246 if (empty(array_filter($this->config[$cat], function($value) { return !is_null($value); }))) {
247 $this->config[$cat] = null;
248 $this->source[$cat] = $source;
250 } elseif (isset($this->config[$cat])) {
251 $this->config[$cat] = null;
252 $this->source[$cat] = $source;
259 * Returns the whole configuration
261 * @return string[][] The configuration
263 public function getAll(): array
265 return $this->config;
269 * Returns an array with missing categories/Keys
271 * @param string[][] $config The array to check
275 public function keyDiff(array $config): array
279 $categories = array_keys($config);
281 foreach ($categories as $category) {
282 if (is_array($config[$category])) {
283 $keys = array_keys($config[$category]);
285 foreach ($keys as $key) {
286 if (!isset($this->config[$category][$key])) {
287 $return[$category][$key] = $config[$category][$key];
297 * Merges a new Cache into the existing one and returns the merged Cache
299 * @param Cache $cache The cache, which should get merged into this Cache
301 * @return Cache The merged Cache
303 public function merge(Cache $cache): Cache
305 $newConfig = $this->config;
306 $newSource = $this->source;
308 $categories = array_keys($cache->config);
310 foreach ($categories as $category) {
311 if (is_array($cache->config[$category])) {
312 $keys = array_keys($cache->config[$category]);
314 foreach ($keys as $key) {
315 $newConfig[$category][$key] = $cache->config[$category][$key];
316 $newSource[$category][$key] = $cache->source[$category][$key];
319 $newConfig[$category] = $cache->config[$category];
320 $newSource[$category] = $cache->source[$category];
324 $newCache = new Cache();
325 $newCache->config = $newConfig;
326 $newCache->source = $newSource;