Completed
Push — master ( 69ec2c...4a399a )
by Maxence
43:16
created
lib/private/AppConfig.php 1 patch
Indentation   +1794 added lines, -1794 removed lines patch added patch discarded remove patch
@@ -51,1798 +51,1798 @@
 block discarded – undo
51 51
  * @since 29.0.0 - Supporting types and lazy loading
52 52
  */
53 53
 class AppConfig implements IAppConfig {
54
-	private const APP_MAX_LENGTH = 32;
55
-	private const KEY_MAX_LENGTH = 64;
56
-	private const ENCRYPTION_PREFIX = '$AppConfigEncryption$';
57
-	private const ENCRYPTION_PREFIX_LENGTH = 21; // strlen(self::ENCRYPTION_PREFIX)
58
-	private const LOCAL_CACHE_KEY = 'OC\\AppConfig';
59
-	private const LOCAL_CACHE_TTL = 3;
60
-
61
-	/** @var array<string, array<string, string>> ['app_id' => ['config_key' => 'config_value']] */
62
-	private array $fastCache = [];   // cache for normal config keys
63
-	/** @var array<string, array<string, string>> ['app_id' => ['config_key' => 'config_value']] */
64
-	private array $lazyCache = [];   // cache for lazy config keys
65
-	/** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */
66
-	private array $valueTypes = [];  // type for all config values
67
-	private bool $fastLoaded = false;
68
-	private bool $lazyLoaded = false;
69
-	/** @var array<string, array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
70
-	private array $configLexiconDetails = [];
71
-	private bool $ignoreLexiconAliases = false;
72
-	private array $strictnessApplied = [];
73
-
74
-	/** @var ?array<string, string> */
75
-	private ?array $appVersionsCache = null;
76
-	private ?ICache $localCache = null;
77
-
78
-	public function __construct(
79
-		protected IDBConnection $connection,
80
-		protected IConfig $config,
81
-		private readonly ConfigManager $configManager,
82
-		private readonly PresetManager $presetManager,
83
-		protected LoggerInterface $logger,
84
-		protected ICrypto $crypto,
85
-		readonly CacheFactory $cacheFactory,
86
-	) {
87
-		if ($config->getSystemValueBool('cache_app_config', true) && $cacheFactory->isLocalCacheAvailable()) {
88
-			$cacheFactory->withServerVersionPrefix(function (ICacheFactory $factory) {
89
-				$this->localCache = $factory->createLocal();
90
-			});
91
-		}
92
-	}
93
-
94
-	/**
95
-	 * @inheritDoc
96
-	 *
97
-	 * @return list<string> list of app ids
98
-	 * @since 7.0.0
99
-	 */
100
-	public function getApps(): array {
101
-		$this->loadConfig(lazy: true);
102
-		$apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache));
103
-		sort($apps);
104
-
105
-		return array_values(array_unique($apps));
106
-	}
107
-
108
-	/**
109
-	 * @inheritDoc
110
-	 *
111
-	 * @param string $app id of the app
112
-	 * @return list<string> list of stored config keys
113
-	 * @see searchKeys to not load lazy config keys
114
-	 *
115
-	 * @since 29.0.0
116
-	 */
117
-	public function getKeys(string $app): array {
118
-		$this->assertParams($app);
119
-		$this->loadConfig($app, true);
120
-		$keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? []));
121
-		sort($keys);
122
-
123
-		return array_values(array_unique($keys));
124
-	}
125
-
126
-	/**
127
-	 * @inheritDoc
128
-	 *
129
-	 * @param string $app id of the app
130
-	 * @param string $prefix returns only keys starting with this value
131
-	 * @param bool $lazy TRUE to search in lazy config keys
132
-	 * @return list<string> list of stored config keys
133
-	 * @since 32.0.0
134
-	 */
135
-	public function searchKeys(string $app, string $prefix = '', bool $lazy = false): array {
136
-		$this->assertParams($app);
137
-		$this->loadConfig($app, $lazy);
138
-		if ($lazy) {
139
-			$keys = array_keys($this->lazyCache[$app] ?? []);
140
-		} else {
141
-			$keys = array_keys($this->fastCache[$app] ?? []);
142
-		}
143
-
144
-		if ($prefix !== '') {
145
-			$keys = array_filter($keys, static fn (string $key): bool => str_starts_with($key, $prefix));
146
-		}
147
-
148
-		sort($keys);
149
-		return array_values(array_unique($keys));
150
-	}
151
-
152
-	/**
153
-	 * @inheritDoc
154
-	 *
155
-	 * @param string $app id of the app
156
-	 * @param string $key config key
157
-	 * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
158
-	 *
159
-	 * @return bool TRUE if key exists
160
-	 * @since 7.0.0
161
-	 * @since 29.0.0 Added the $lazy argument
162
-	 */
163
-	public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
164
-		$this->assertParams($app, $key);
165
-		$this->loadConfig($app, $lazy ?? true);
166
-		$this->matchAndApplyLexiconDefinition($app, $key);
167
-
168
-		$hasLazy = isset($this->lazyCache[$app][$key]);
169
-		$hasFast = isset($this->fastCache[$app][$key]);
170
-		if ($lazy === null) {
171
-			return $hasLazy || $hasFast;
172
-		} else {
173
-			return $lazy ? $hasLazy : $hasFast;
174
-		}
175
-	}
176
-
177
-	/**
178
-	 * @param string $app id of the app
179
-	 * @param string $key config key
180
-	 * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
181
-	 *
182
-	 * @return bool
183
-	 * @throws AppConfigUnknownKeyException if config key is not known
184
-	 * @since 29.0.0
185
-	 */
186
-	public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
187
-		$this->assertParams($app, $key);
188
-		$this->loadConfig(null, $lazy ?? true);
189
-		$this->matchAndApplyLexiconDefinition($app, $key);
190
-
191
-		if (!isset($this->valueTypes[$app][$key])) {
192
-			throw new AppConfigUnknownKeyException('unknown config key');
193
-		}
194
-
195
-		return $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key]);
196
-	}
197
-
198
-	/**
199
-	 * @inheritDoc
200
-	 *
201
-	 * @param string $app if of the app
202
-	 * @param string $key config key
203
-	 *
204
-	 * @return bool TRUE if config is lazy loaded
205
-	 * @throws AppConfigUnknownKeyException if config key is not known
206
-	 * @see IAppConfig for details about lazy loading
207
-	 * @since 29.0.0
208
-	 */
209
-	public function isLazy(string $app, string $key): bool {
210
-		$this->assertParams($app, $key);
211
-		$this->matchAndApplyLexiconDefinition($app, $key);
212
-
213
-		// there is a huge probability the non-lazy config are already loaded
214
-		if ($this->hasKey($app, $key, false)) {
215
-			return false;
216
-		}
217
-
218
-		// key not found, we search in the lazy config
219
-		if ($this->hasKey($app, $key, true)) {
220
-			return true;
221
-		}
222
-
223
-		throw new AppConfigUnknownKeyException('unknown config key');
224
-	}
225
-
226
-
227
-	/**
228
-	 * @inheritDoc
229
-	 *
230
-	 * @param string $app id of the app
231
-	 * @param string $prefix config keys prefix to search
232
-	 * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE}
233
-	 *
234
-	 * @return array<string, string|int|float|bool|array> [configKey => configValue]
235
-	 * @since 29.0.0
236
-	 */
237
-	public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array {
238
-		$this->assertParams($app, $prefix);
239
-		// if we want to filter values, we need to get sensitivity
240
-		$this->loadConfig($app, true);
241
-		// array_merge() will remove numeric keys (here config keys), so addition arrays instead
242
-		$values = $this->formatAppValues($app, ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? []));
243
-		$values = array_filter(
244
-			$values,
245
-			function (string $key) use ($prefix): bool {
246
-				return str_starts_with($key, $prefix); // filter values based on $prefix
247
-			}, ARRAY_FILTER_USE_KEY
248
-		);
249
-
250
-		if (!$filtered) {
251
-			return $values;
252
-		}
253
-
254
-		/**
255
-		 * Using the old (deprecated) list of sensitive values.
256
-		 */
257
-		foreach ($this->getSensitiveKeys($app) as $sensitiveKeyExp) {
258
-			$sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values));
259
-			foreach ($sensitiveKeys as $sensitiveKey) {
260
-				$this->valueTypes[$app][$sensitiveKey] = ($this->valueTypes[$app][$sensitiveKey] ?? 0) | self::VALUE_SENSITIVE;
261
-			}
262
-		}
263
-
264
-		$result = [];
265
-		foreach ($values as $key => $value) {
266
-			$result[$key] = $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key] ?? 0) ? IConfig::SENSITIVE_VALUE : $value;
267
-		}
268
-
269
-		return $result;
270
-	}
271
-
272
-	/**
273
-	 * @inheritDoc
274
-	 *
275
-	 * @param string $key config key
276
-	 * @param bool $lazy search within lazy loaded config
277
-	 * @param int|null $typedAs enforce type for the returned values ({@see self::VALUE_STRING} and others)
278
-	 *
279
-	 * @return array<string, string|int|float|bool|array> [appId => configValue]
280
-	 * @since 29.0.0
281
-	 */
282
-	public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array {
283
-		$this->assertParams('', $key, true);
284
-		$this->loadConfig(null, $lazy);
285
-
286
-		/** @var array<array-key, array<array-key, mixed>> $cache */
287
-		if ($lazy) {
288
-			$cache = $this->lazyCache;
289
-		} else {
290
-			$cache = $this->fastCache;
291
-		}
292
-
293
-		$values = [];
294
-		foreach (array_keys($cache) as $app) {
295
-			if (isset($cache[$app][$key])) {
296
-				$values[$app] = $this->convertTypedValue($cache[$app][$key], $typedAs ?? $this->getValueType((string)$app, $key, $lazy));
297
-			}
298
-		}
299
-
300
-		return $values;
301
-	}
302
-
303
-
304
-	/**
305
-	 * Get the config value as string.
306
-	 * If the value does not exist the given default will be returned.
307
-	 *
308
-	 * Set lazy to `null` to ignore it and get the value from either source.
309
-	 *
310
-	 * **WARNING:** Method is internal and **SHOULD** not be used, as it is better to get the value with a type.
311
-	 *
312
-	 * @param string $app id of the app
313
-	 * @param string $key config key
314
-	 * @param string $default config value
315
-	 * @param null|bool $lazy get config as lazy loaded or not. can be NULL
316
-	 *
317
-	 * @return string the value or $default
318
-	 * @internal
319
-	 * @since 29.0.0
320
-	 * @see IAppConfig for explanation about lazy loading
321
-	 * @see getValueString()
322
-	 * @see getValueInt()
323
-	 * @see getValueFloat()
324
-	 * @see getValueBool()
325
-	 * @see getValueArray()
326
-	 */
327
-	public function getValueMixed(
328
-		string $app,
329
-		string $key,
330
-		string $default = '',
331
-		?bool $lazy = false,
332
-	): string {
333
-		try {
334
-			$lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy;
335
-		} catch (AppConfigUnknownKeyException) {
336
-			return $default;
337
-		}
338
-
339
-		return $this->getTypedValue(
340
-			$app,
341
-			$key,
342
-			$default,
343
-			$lazy,
344
-			self::VALUE_MIXED
345
-		);
346
-	}
347
-
348
-	/**
349
-	 * @inheritDoc
350
-	 *
351
-	 * @param string $app id of the app
352
-	 * @param string $key config key
353
-	 * @param string $default default value
354
-	 * @param bool $lazy search within lazy loaded config
355
-	 *
356
-	 * @return string stored config value or $default if not set in database
357
-	 * @throws InvalidArgumentException if one of the argument format is invalid
358
-	 * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
359
-	 * @since 29.0.0
360
-	 * @see IAppConfig for explanation about lazy loading
361
-	 */
362
-	public function getValueString(
363
-		string $app,
364
-		string $key,
365
-		string $default = '',
366
-		bool $lazy = false,
367
-	): string {
368
-		return $this->getTypedValue($app, $key, $default, $lazy, self::VALUE_STRING);
369
-	}
370
-
371
-	/**
372
-	 * @inheritDoc
373
-	 *
374
-	 * @param string $app id of the app
375
-	 * @param string $key config key
376
-	 * @param int $default default value
377
-	 * @param bool $lazy search within lazy loaded config
378
-	 *
379
-	 * @return int stored config value or $default if not set in database
380
-	 * @throws InvalidArgumentException if one of the argument format is invalid
381
-	 * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
382
-	 * @since 29.0.0
383
-	 * @see IAppConfig for explanation about lazy loading
384
-	 */
385
-	public function getValueInt(
386
-		string $app,
387
-		string $key,
388
-		int $default = 0,
389
-		bool $lazy = false,
390
-	): int {
391
-		return (int)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_INT);
392
-	}
393
-
394
-	/**
395
-	 * @inheritDoc
396
-	 *
397
-	 * @param string $app id of the app
398
-	 * @param string $key config key
399
-	 * @param float $default default value
400
-	 * @param bool $lazy search within lazy loaded config
401
-	 *
402
-	 * @return float stored config value or $default if not set in database
403
-	 * @throws InvalidArgumentException if one of the argument format is invalid
404
-	 * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
405
-	 * @since 29.0.0
406
-	 * @see IAppConfig for explanation about lazy loading
407
-	 */
408
-	public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float {
409
-		return (float)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_FLOAT);
410
-	}
411
-
412
-	/**
413
-	 * @inheritDoc
414
-	 *
415
-	 * @param string $app id of the app
416
-	 * @param string $key config key
417
-	 * @param bool $default default value
418
-	 * @param bool $lazy search within lazy loaded config
419
-	 *
420
-	 * @return bool stored config value or $default if not set in database
421
-	 * @throws InvalidArgumentException if one of the argument format is invalid
422
-	 * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
423
-	 * @since 29.0.0
424
-	 * @see IAppConfig for explanation about lazy loading
425
-	 */
426
-	public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool {
427
-		$b = strtolower($this->getTypedValue($app, $key, $default ? 'true' : 'false', $lazy, self::VALUE_BOOL));
428
-		return in_array($b, ['1', 'true', 'yes', 'on']);
429
-	}
430
-
431
-	/**
432
-	 * @inheritDoc
433
-	 *
434
-	 * @param string $app id of the app
435
-	 * @param string $key config key
436
-	 * @param array $default default value
437
-	 * @param bool $lazy search within lazy loaded config
438
-	 *
439
-	 * @return array stored config value or $default if not set in database
440
-	 * @throws InvalidArgumentException if one of the argument format is invalid
441
-	 * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
442
-	 * @since 29.0.0
443
-	 * @see IAppConfig for explanation about lazy loading
444
-	 */
445
-	public function getValueArray(
446
-		string $app,
447
-		string $key,
448
-		array $default = [],
449
-		bool $lazy = false,
450
-	): array {
451
-		try {
452
-			$defaultJson = json_encode($default, JSON_THROW_ON_ERROR);
453
-			$value = json_decode($this->getTypedValue($app, $key, $defaultJson, $lazy, self::VALUE_ARRAY), true, flags: JSON_THROW_ON_ERROR);
454
-
455
-			return is_array($value) ? $value : [];
456
-		} catch (JsonException) {
457
-			return [];
458
-		}
459
-	}
460
-
461
-	/**
462
-	 * @param string $app id of the app
463
-	 * @param string $key config key
464
-	 * @param string $default default value
465
-	 * @param bool $lazy search within lazy loaded config
466
-	 * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT}{@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
467
-	 *
468
-	 * @return string
469
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
470
-	 * @throws InvalidArgumentException
471
-	 */
472
-	private function getTypedValue(
473
-		string $app,
474
-		string $key,
475
-		string $default,
476
-		bool $lazy,
477
-		int $type,
478
-	): string {
479
-		$this->assertParams($app, $key, valueType: $type);
480
-		$origKey = $key;
481
-		$matched = $this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $default);
482
-		if ($default === null) {
483
-			// there is no logical reason for it to be null
484
-			throw new \Exception('default cannot be null');
485
-		}
486
-
487
-		// returns default if strictness of lexicon is set to WARNING (block and report)
488
-		if (!$matched) {
489
-			return $default;
490
-		}
491
-
492
-		$this->loadConfig($app, $lazy ?? true);
493
-
494
-		/**
495
-		 * We ignore check if mixed type is requested.
496
-		 * If type of stored value is set as mixed, we don't filter.
497
-		 * If type of stored value is defined, we compare with the one requested.
498
-		 */
499
-		$knownType = $this->valueTypes[$app][$key] ?? 0;
500
-		if (!$this->isTyped(self::VALUE_MIXED, $type)
501
-			&& $knownType > 0
502
-			&& !$this->isTyped(self::VALUE_MIXED, $knownType)
503
-			&& !$this->isTyped($type, $knownType)) {
504
-			$this->logger->warning('conflict with value type from database', ['app' => $app, 'key' => $key, 'type' => $type, 'knownType' => $knownType]);
505
-			throw new AppConfigTypeConflictException('conflict with value type from database');
506
-		}
507
-
508
-		/**
509
-		 * - the pair $app/$key cannot exist in both array,
510
-		 * - we should still return an existing non-lazy value even if current method
511
-		 *   is called with $lazy is true
512
-		 *
513
-		 * This way, lazyCache will be empty until the load for lazy config value is requested.
514
-		 */
515
-		if (isset($this->lazyCache[$app][$key])) {
516
-			$value = $this->lazyCache[$app][$key];
517
-		} elseif (isset($this->fastCache[$app][$key])) {
518
-			$value = $this->fastCache[$app][$key];
519
-		} else {
520
-			return $default;
521
-		}
522
-
523
-		$sensitive = $this->isTyped(self::VALUE_SENSITIVE, $knownType);
524
-		if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) {
525
-			// Only decrypt values that are stored encrypted
526
-			$value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
527
-		}
528
-
529
-		// in case the key was modified while running matchAndApplyLexiconDefinition() we are
530
-		// interested to check options in case a modification of the value is needed
531
-		// ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN
532
-		if ($origKey !== $key && $type === self::VALUE_BOOL) {
533
-			$value = ($this->configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0';
534
-		}
535
-
536
-		return $value;
537
-	}
538
-
539
-	/**
540
-	 * @inheritDoc
541
-	 *
542
-	 * @param string $app id of the app
543
-	 * @param string $key config key
544
-	 *
545
-	 * @return int type of the value
546
-	 * @throws AppConfigUnknownKeyException if config key is not known
547
-	 * @since 29.0.0
548
-	 * @see VALUE_STRING
549
-	 * @see VALUE_INT
550
-	 * @see VALUE_FLOAT
551
-	 * @see VALUE_BOOL
552
-	 * @see VALUE_ARRAY
553
-	 */
554
-	public function getValueType(string $app, string $key, ?bool $lazy = null): int {
555
-		$type = self::VALUE_MIXED;
556
-		$ignorable = $lazy ?? false;
557
-		$this->matchAndApplyLexiconDefinition($app, $key, $ignorable, $type);
558
-		if ($type !== self::VALUE_MIXED) {
559
-			// a modified $type means config key is set in Lexicon
560
-			return $type;
561
-		}
562
-
563
-		$this->assertParams($app, $key);
564
-		$this->loadConfig($app, $lazy ?? true);
565
-
566
-		if (!isset($this->valueTypes[$app][$key])) {
567
-			throw new AppConfigUnknownKeyException('unknown config key');
568
-		}
569
-
570
-		$type = $this->valueTypes[$app][$key];
571
-		$type &= ~self::VALUE_SENSITIVE;
572
-		return $type;
573
-	}
574
-
575
-
576
-	/**
577
-	 * Store a config key and its value in database as VALUE_MIXED
578
-	 *
579
-	 * **WARNING:** Method is internal and **MUST** not be used as it is best to set a real value type
580
-	 *
581
-	 * @param string $app id of the app
582
-	 * @param string $key config key
583
-	 * @param string $value config value
584
-	 * @param bool $lazy set config as lazy loaded
585
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
586
-	 *
587
-	 * @return bool TRUE if value was different, therefor updated in database
588
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED
589
-	 * @internal
590
-	 * @since 29.0.0
591
-	 * @see IAppConfig for explanation about lazy loading
592
-	 * @see setValueString()
593
-	 * @see setValueInt()
594
-	 * @see setValueFloat()
595
-	 * @see setValueBool()
596
-	 * @see setValueArray()
597
-	 */
598
-	public function setValueMixed(
599
-		string $app,
600
-		string $key,
601
-		string $value,
602
-		bool $lazy = false,
603
-		bool $sensitive = false,
604
-	): bool {
605
-		return $this->setTypedValue(
606
-			$app,
607
-			$key,
608
-			$value,
609
-			$lazy,
610
-			self::VALUE_MIXED | ($sensitive ? self::VALUE_SENSITIVE : 0)
611
-		);
612
-	}
613
-
614
-
615
-	/**
616
-	 * @inheritDoc
617
-	 *
618
-	 * @param string $app id of the app
619
-	 * @param string $key config key
620
-	 * @param string $value config value
621
-	 * @param bool $lazy set config as lazy loaded
622
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
623
-	 *
624
-	 * @return bool TRUE if value was different, therefor updated in database
625
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
626
-	 * @since 29.0.0
627
-	 * @see IAppConfig for explanation about lazy loading
628
-	 */
629
-	public function setValueString(
630
-		string $app,
631
-		string $key,
632
-		string $value,
633
-		bool $lazy = false,
634
-		bool $sensitive = false,
635
-	): bool {
636
-		return $this->setTypedValue(
637
-			$app,
638
-			$key,
639
-			$value,
640
-			$lazy,
641
-			self::VALUE_STRING | ($sensitive ? self::VALUE_SENSITIVE : 0)
642
-		);
643
-	}
644
-
645
-	/**
646
-	 * @inheritDoc
647
-	 *
648
-	 * @param string $app id of the app
649
-	 * @param string $key config key
650
-	 * @param int $value config value
651
-	 * @param bool $lazy set config as lazy loaded
652
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
653
-	 *
654
-	 * @return bool TRUE if value was different, therefor updated in database
655
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
656
-	 * @since 29.0.0
657
-	 * @see IAppConfig for explanation about lazy loading
658
-	 */
659
-	public function setValueInt(
660
-		string $app,
661
-		string $key,
662
-		int $value,
663
-		bool $lazy = false,
664
-		bool $sensitive = false,
665
-	): bool {
666
-		if ($value > 2000000000) {
667
-			$this->logger->debug('You are trying to store an integer value around/above 2,147,483,647. This is a reminder that reaching this theoretical limit on 32 bits system will throw an exception.');
668
-		}
669
-
670
-		return $this->setTypedValue(
671
-			$app,
672
-			$key,
673
-			(string)$value,
674
-			$lazy,
675
-			self::VALUE_INT | ($sensitive ? self::VALUE_SENSITIVE : 0)
676
-		);
677
-	}
678
-
679
-	/**
680
-	 * @inheritDoc
681
-	 *
682
-	 * @param string $app id of the app
683
-	 * @param string $key config key
684
-	 * @param float $value config value
685
-	 * @param bool $lazy set config as lazy loaded
686
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
687
-	 *
688
-	 * @return bool TRUE if value was different, therefor updated in database
689
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
690
-	 * @since 29.0.0
691
-	 * @see IAppConfig for explanation about lazy loading
692
-	 */
693
-	public function setValueFloat(
694
-		string $app,
695
-		string $key,
696
-		float $value,
697
-		bool $lazy = false,
698
-		bool $sensitive = false,
699
-	): bool {
700
-		return $this->setTypedValue(
701
-			$app,
702
-			$key,
703
-			(string)$value,
704
-			$lazy,
705
-			self::VALUE_FLOAT | ($sensitive ? self::VALUE_SENSITIVE : 0)
706
-		);
707
-	}
708
-
709
-	/**
710
-	 * @inheritDoc
711
-	 *
712
-	 * @param string $app id of the app
713
-	 * @param string $key config key
714
-	 * @param bool $value config value
715
-	 * @param bool $lazy set config as lazy loaded
716
-	 *
717
-	 * @return bool TRUE if value was different, therefor updated in database
718
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
719
-	 * @since 29.0.0
720
-	 * @see IAppConfig for explanation about lazy loading
721
-	 */
722
-	public function setValueBool(
723
-		string $app,
724
-		string $key,
725
-		bool $value,
726
-		bool $lazy = false,
727
-	): bool {
728
-		return $this->setTypedValue(
729
-			$app,
730
-			$key,
731
-			($value) ? '1' : '0',
732
-			$lazy,
733
-			self::VALUE_BOOL
734
-		);
735
-	}
736
-
737
-	/**
738
-	 * @inheritDoc
739
-	 *
740
-	 * @param string $app id of the app
741
-	 * @param string $key config key
742
-	 * @param array $value config value
743
-	 * @param bool $lazy set config as lazy loaded
744
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
745
-	 *
746
-	 * @return bool TRUE if value was different, therefor updated in database
747
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
748
-	 * @throws JsonException
749
-	 * @since 29.0.0
750
-	 * @see IAppConfig for explanation about lazy loading
751
-	 */
752
-	public function setValueArray(
753
-		string $app,
754
-		string $key,
755
-		array $value,
756
-		bool $lazy = false,
757
-		bool $sensitive = false,
758
-	): bool {
759
-		try {
760
-			return $this->setTypedValue(
761
-				$app,
762
-				$key,
763
-				json_encode($value, JSON_THROW_ON_ERROR),
764
-				$lazy,
765
-				self::VALUE_ARRAY | ($sensitive ? self::VALUE_SENSITIVE : 0)
766
-			);
767
-		} catch (JsonException $e) {
768
-			$this->logger->warning('could not setValueArray', ['app' => $app, 'key' => $key, 'exception' => $e]);
769
-			throw $e;
770
-		}
771
-	}
772
-
773
-	/**
774
-	 * Store a config key and its value in database
775
-	 *
776
-	 * If config key is already known with the exact same config value and same sensitive/lazy status, the
777
-	 * database is not updated. If config value was previously stored as sensitive, status will not be
778
-	 * altered.
779
-	 *
780
-	 * @param string $app id of the app
781
-	 * @param string $key config key
782
-	 * @param string $value config value
783
-	 * @param bool $lazy config set as lazy loaded
784
-	 * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
785
-	 *
786
-	 * @return bool TRUE if value was updated in database
787
-	 * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
788
-	 * @see IAppConfig for explanation about lazy loading
789
-	 */
790
-	private function setTypedValue(
791
-		string $app,
792
-		string $key,
793
-		string $value,
794
-		bool $lazy,
795
-		int $type,
796
-	): bool {
797
-		$this->assertParams($app, $key);
798
-		if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type)) {
799
-			return false; // returns false as database is not updated
800
-		}
801
-		$this->loadConfig(null, $lazy ?? true);
802
-
803
-		$sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type);
804
-		$inserted = $refreshCache = false;
805
-
806
-		$origValue = $value;
807
-		if ($sensitive || ($this->hasKey($app, $key, $lazy) && $this->isSensitive($app, $key, $lazy))) {
808
-			$value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value);
809
-		}
810
-
811
-		if ($this->hasKey($app, $key, $lazy)) {
812
-			/**
813
-			 * no update if key is already known with set lazy status and value is
814
-			 * not different, unless sensitivity is switched from false to true.
815
-			 */
816
-			if ($origValue === $this->getTypedValue($app, $key, $value, $lazy ?? true, $type)
817
-				&& (!$sensitive || $this->isSensitive($app, $key, $lazy))) {
818
-				return false;
819
-			}
820
-		} else {
821
-			/**
822
-			 * if key is not known yet, we try to insert.
823
-			 * It might fail if the key exists with a different lazy flag.
824
-			 */
825
-			try {
826
-				$insert = $this->connection->getQueryBuilder();
827
-				$insert->insert('appconfig')
828
-					->setValue('appid', $insert->createNamedParameter($app))
829
-					->setValue('lazy', $insert->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT))
830
-					->setValue('type', $insert->createNamedParameter($type, IQueryBuilder::PARAM_INT))
831
-					->setValue('configkey', $insert->createNamedParameter($key))
832
-					->setValue('configvalue', $insert->createNamedParameter($value));
833
-				$insert->executeStatement();
834
-				$inserted = true;
835
-			} catch (DBException $e) {
836
-				if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
837
-					throw $e; // TODO: throw exception or just log and returns false !?
838
-				}
839
-			}
840
-		}
841
-
842
-		/**
843
-		 * We cannot insert a new row, meaning we need to update an already existing one
844
-		 */
845
-		if (!$inserted) {
846
-			$currType = $this->valueTypes[$app][$key] ?? 0;
847
-			if ($currType === 0) { // this might happen when switching lazy loading status
848
-				$this->loadConfig(lazy: true);
849
-				$currType = $this->valueTypes[$app][$key] ?? 0;
850
-			}
851
-
852
-			/**
853
-			 * This should only happen during the upgrade process from 28 to 29.
854
-			 * We only log a warning and set it to VALUE_MIXED.
855
-			 */
856
-			if ($currType === 0) {
857
-				$this->logger->warning('Value type is set to zero (0) in database. This is fine only during the upgrade process from 28 to 29.', ['app' => $app, 'key' => $key]);
858
-				$currType = self::VALUE_MIXED;
859
-			}
860
-
861
-			/**
862
-			 * we only accept a different type from the one stored in database
863
-			 * if the one stored in database is not-defined (VALUE_MIXED)
864
-			 */
865
-			if (!$this->isTyped(self::VALUE_MIXED, $currType)
866
-				&& ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
867
-				try {
868
-					$currType = $this->convertTypeToString($currType);
869
-					$type = $this->convertTypeToString($type);
870
-				} catch (AppConfigIncorrectTypeException) {
871
-					// can be ignored, this was just needed for a better exception message.
872
-				}
873
-				throw new AppConfigTypeConflictException('conflict between new type (' . $type . ') and old type (' . $currType . ')');
874
-			}
875
-
876
-			// we fix $type if the stored value, or the new value as it might be changed, is set as sensitive
877
-			if ($sensitive || $this->isTyped(self::VALUE_SENSITIVE, $currType)) {
878
-				$type |= self::VALUE_SENSITIVE;
879
-			}
880
-
881
-			try {
882
-				if ($lazy !== $this->isLazy($app, $key)) {
883
-					$refreshCache = true;
884
-				}
885
-			} catch (AppConfigUnknownKeyException) {
886
-				// pass
887
-			}
888
-
889
-			$update = $this->connection->getQueryBuilder();
890
-			$update->update('appconfig')
891
-				->set('configvalue', $update->createNamedParameter($value))
892
-				->set('lazy', $update->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT))
893
-				->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
894
-				->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
895
-				->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
896
-
897
-			$update->executeStatement();
898
-		}
899
-
900
-		if ($refreshCache) {
901
-			$this->clearCache();
902
-			return true;
903
-		}
904
-
905
-		// update local cache
906
-		if ($lazy) {
907
-			$this->lazyCache[$app][$key] = $value;
908
-		} else {
909
-			$this->fastCache[$app][$key] = $value;
910
-		}
911
-		$this->valueTypes[$app][$key] = $type;
912
-		$this->clearLocalCache();
913
-
914
-		return true;
915
-	}
916
-
917
-	/**
918
-	 * Change the type of config value.
919
-	 *
920
-	 * **WARNING:** Method is internal and **MUST** not be used as it may break things.
921
-	 *
922
-	 * @param string $app id of the app
923
-	 * @param string $key config key
924
-	 * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
925
-	 *
926
-	 * @return bool TRUE if database update were necessary
927
-	 * @throws AppConfigUnknownKeyException if $key is now known in database
928
-	 * @throws AppConfigIncorrectTypeException if $type is not valid
929
-	 * @internal
930
-	 * @since 29.0.0
931
-	 */
932
-	public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
933
-		$this->assertParams($app, $key);
934
-		$this->loadConfig(lazy: true);
935
-		$this->matchAndApplyLexiconDefinition($app, $key);
936
-		$this->isLazy($app, $key); // confirm key exists
937
-
938
-		// type can only be one type
939
-		if (!in_array($type, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
940
-			throw new AppConfigIncorrectTypeException('Unknown value type');
941
-		}
942
-
943
-		$currType = $this->valueTypes[$app][$key];
944
-		if (($type | self::VALUE_SENSITIVE) === ($currType | self::VALUE_SENSITIVE)) {
945
-			return false;
946
-		}
947
-
948
-		// we complete with sensitive flag if the stored value is set as sensitive
949
-		if ($this->isTyped(self::VALUE_SENSITIVE, $currType)) {
950
-			$type = $type | self::VALUE_SENSITIVE;
951
-		}
952
-
953
-		$update = $this->connection->getQueryBuilder();
954
-		$update->update('appconfig')
955
-			->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
956
-			->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
957
-			->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
958
-		$update->executeStatement();
959
-		$this->valueTypes[$app][$key] = $type;
960
-
961
-		return true;
962
-	}
963
-
964
-
965
-	/**
966
-	 * @inheritDoc
967
-	 *
968
-	 * @param string $app id of the app
969
-	 * @param string $key config key
970
-	 * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
971
-	 *
972
-	 * @return bool TRUE if entry was found in database and an update was necessary
973
-	 * @since 29.0.0
974
-	 */
975
-	public function updateSensitive(string $app, string $key, bool $sensitive): bool {
976
-		$this->assertParams($app, $key);
977
-		$this->loadConfig(lazy: true);
978
-		$this->matchAndApplyLexiconDefinition($app, $key);
979
-
980
-		try {
981
-			if ($sensitive === $this->isSensitive($app, $key, null)) {
982
-				return false;
983
-			}
984
-		} catch (AppConfigUnknownKeyException $e) {
985
-			return false;
986
-		}
987
-
988
-		$lazy = $this->isLazy($app, $key);
989
-		if ($lazy) {
990
-			$cache = $this->lazyCache;
991
-		} else {
992
-			$cache = $this->fastCache;
993
-		}
994
-
995
-		if (!isset($cache[$app][$key])) {
996
-			throw new AppConfigUnknownKeyException('unknown config key');
997
-		}
998
-
999
-		/**
1000
-		 * type returned by getValueType() is already cleaned from sensitive flag
1001
-		 * we just need to update it based on $sensitive and store it in database
1002
-		 */
1003
-		$type = $this->getValueType($app, $key);
1004
-		$value = $cache[$app][$key];
1005
-		if ($sensitive) {
1006
-			$type |= self::VALUE_SENSITIVE;
1007
-			$value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value);
1008
-		} else {
1009
-			$value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
1010
-		}
1011
-
1012
-		$update = $this->connection->getQueryBuilder();
1013
-		$update->update('appconfig')
1014
-			->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
1015
-			->set('configvalue', $update->createNamedParameter($value))
1016
-			->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
1017
-			->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
1018
-		$update->executeStatement();
1019
-
1020
-		$this->valueTypes[$app][$key] = $type;
1021
-
1022
-		return true;
1023
-	}
1024
-
1025
-	/**
1026
-	 * @inheritDoc
1027
-	 *
1028
-	 * @param string $app id of the app
1029
-	 * @param string $key config key
1030
-	 * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
1031
-	 *
1032
-	 * @return bool TRUE if entry was found in database and an update was necessary
1033
-	 * @since 29.0.0
1034
-	 */
1035
-	public function updateLazy(string $app, string $key, bool $lazy): bool {
1036
-		$this->assertParams($app, $key);
1037
-		$this->loadConfig(lazy: true);
1038
-		$this->matchAndApplyLexiconDefinition($app, $key);
1039
-
1040
-		try {
1041
-			if ($lazy === $this->isLazy($app, $key)) {
1042
-				return false;
1043
-			}
1044
-		} catch (AppConfigUnknownKeyException $e) {
1045
-			return false;
1046
-		}
1047
-
1048
-		$update = $this->connection->getQueryBuilder();
1049
-		$update->update('appconfig')
1050
-			->set('lazy', $update->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT))
1051
-			->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
1052
-			->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
1053
-		$update->executeStatement();
1054
-
1055
-		// At this point, it is a lot safer to clean cache
1056
-		$this->clearCache();
1057
-
1058
-		return true;
1059
-	}
1060
-
1061
-	/**
1062
-	 * @inheritDoc
1063
-	 *
1064
-	 * @param string $app id of the app
1065
-	 * @param string $key config key
1066
-	 *
1067
-	 * @return array
1068
-	 * @throws AppConfigUnknownKeyException if config key is not known in database
1069
-	 * @since 29.0.0
1070
-	 */
1071
-	public function getDetails(string $app, string $key): array {
1072
-		$this->assertParams($app, $key);
1073
-		$this->loadConfig(lazy: true);
1074
-		$this->matchAndApplyLexiconDefinition($app, $key);
1075
-		$lazy = $this->isLazy($app, $key);
1076
-
1077
-		if ($lazy) {
1078
-			$cache = $this->lazyCache;
1079
-		} else {
1080
-			$cache = $this->fastCache;
1081
-		}
1082
-
1083
-		$type = $this->getValueType($app, $key);
1084
-		try {
1085
-			$typeString = $this->convertTypeToString($type);
1086
-		} catch (AppConfigIncorrectTypeException $e) {
1087
-			$this->logger->warning('type stored in database is not correct', ['exception' => $e, 'type' => $type]);
1088
-			$typeString = (string)$type;
1089
-		}
1090
-
1091
-		if (!isset($cache[$app][$key])) {
1092
-			throw new AppConfigUnknownKeyException('unknown config key');
1093
-		}
1094
-
1095
-		$value = $cache[$app][$key];
1096
-		$sensitive = $this->isSensitive($app, $key, null);
1097
-		if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) {
1098
-			$value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
1099
-		}
1100
-
1101
-		return [
1102
-			'app' => $app,
1103
-			'key' => $key,
1104
-			'value' => $value,
1105
-			'type' => $type,
1106
-			'lazy' => $lazy,
1107
-			'typeString' => $typeString,
1108
-			'sensitive' => $sensitive
1109
-		];
1110
-	}
1111
-
1112
-	/**
1113
-	 * @inheritDoc
1114
-	 *
1115
-	 * @param string $app id of the app
1116
-	 * @param string $key config key
1117
-	 *
1118
-	 * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, internal?: bool, default?: string, definition?: string, note?: string}
1119
-	 * @since 32.0.0
1120
-	 */
1121
-	public function getKeyDetails(string $app, string $key): array {
1122
-		$this->assertParams($app, $key);
1123
-		try {
1124
-			$details = $this->getDetails($app, $key);
1125
-		} catch (AppConfigUnknownKeyException) {
1126
-			$details = [
1127
-				'app' => $app,
1128
-				'key' => $key
1129
-			];
1130
-		}
1131
-
1132
-		/** @var Entry $lexiconEntry */
1133
-		try {
1134
-			$lazy = false;
1135
-			$this->matchAndApplyLexiconDefinition($app, $key, $lazy, lexiconEntry: $lexiconEntry);
1136
-		} catch (AppConfigTypeConflictException|AppConfigUnknownKeyException) {
1137
-			// can be ignored
1138
-		}
1139
-
1140
-		if ($lexiconEntry !== null) {
1141
-			$details = array_merge($details, [
1142
-				'lazy' => $lexiconEntry->isLazy(),
1143
-				'valueType' => $lexiconEntry->getValueType(),
1144
-				'valueTypeName' => $lexiconEntry->getValueType()->name,
1145
-				'sensitive' => $lexiconEntry->isFlagged(self::FLAG_SENSITIVE),
1146
-				'internal' => $lexiconEntry->isFlagged(self::FLAG_INTERNAL),
1147
-				'default' => $lexiconEntry->getDefault($this->presetManager->getLexiconPreset()),
1148
-				'definition' => $lexiconEntry->getDefinition(),
1149
-				'note' => $lexiconEntry->getNote(),
1150
-			]);
1151
-		}
1152
-
1153
-		return array_filter($details, static fn ($v): bool => ($v !== null));
1154
-	}
1155
-
1156
-	/**
1157
-	 * @param string $type
1158
-	 *
1159
-	 * @return int
1160
-	 * @throws AppConfigIncorrectTypeException
1161
-	 * @since 29.0.0
1162
-	 */
1163
-	public function convertTypeToInt(string $type): int {
1164
-		return match (strtolower($type)) {
1165
-			'mixed' => IAppConfig::VALUE_MIXED,
1166
-			'string' => IAppConfig::VALUE_STRING,
1167
-			'integer' => IAppConfig::VALUE_INT,
1168
-			'float' => IAppConfig::VALUE_FLOAT,
1169
-			'boolean' => IAppConfig::VALUE_BOOL,
1170
-			'array' => IAppConfig::VALUE_ARRAY,
1171
-			default => throw new AppConfigIncorrectTypeException('Unknown type ' . $type)
1172
-		};
1173
-	}
1174
-
1175
-	/**
1176
-	 * @param int $type
1177
-	 *
1178
-	 * @return string
1179
-	 * @throws AppConfigIncorrectTypeException
1180
-	 * @since 29.0.0
1181
-	 */
1182
-	public function convertTypeToString(int $type): string {
1183
-		$type &= ~self::VALUE_SENSITIVE;
1184
-
1185
-		return match ($type) {
1186
-			IAppConfig::VALUE_MIXED => 'mixed',
1187
-			IAppConfig::VALUE_STRING => 'string',
1188
-			IAppConfig::VALUE_INT => 'integer',
1189
-			IAppConfig::VALUE_FLOAT => 'float',
1190
-			IAppConfig::VALUE_BOOL => 'boolean',
1191
-			IAppConfig::VALUE_ARRAY => 'array',
1192
-			default => throw new AppConfigIncorrectTypeException('Unknown numeric type ' . $type)
1193
-		};
1194
-	}
1195
-
1196
-	/**
1197
-	 * @inheritDoc
1198
-	 *
1199
-	 * @param string $app id of the app
1200
-	 * @param string $key config key
1201
-	 *
1202
-	 * @since 29.0.0
1203
-	 */
1204
-	public function deleteKey(string $app, string $key): void {
1205
-		$this->assertParams($app, $key);
1206
-		$this->matchAndApplyLexiconDefinition($app, $key);
1207
-
1208
-		$qb = $this->connection->getQueryBuilder();
1209
-		$qb->delete('appconfig')
1210
-			->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
1211
-			->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key)));
1212
-		$qb->executeStatement();
1213
-
1214
-		unset($this->lazyCache[$app][$key]);
1215
-		unset($this->fastCache[$app][$key]);
1216
-		unset($this->valueTypes[$app][$key]);
1217
-		$this->clearLocalCache();
1218
-	}
1219
-
1220
-	/**
1221
-	 * @inheritDoc
1222
-	 *
1223
-	 * @param string $app id of the app
1224
-	 *
1225
-	 * @since 29.0.0
1226
-	 */
1227
-	public function deleteApp(string $app): void {
1228
-		$this->assertParams($app);
1229
-		$qb = $this->connection->getQueryBuilder();
1230
-		$qb->delete('appconfig')
1231
-			->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)));
1232
-		$qb->executeStatement();
1233
-
1234
-		$this->clearCache();
1235
-	}
1236
-
1237
-	/**
1238
-	 * @inheritDoc
1239
-	 *
1240
-	 * @param bool $reload set to TRUE to refill cache instantly after clearing it
1241
-	 *
1242
-	 * @internal
1243
-	 * @since 29.0.0
1244
-	 */
1245
-	public function clearCache(bool $reload = false): void {
1246
-		$this->lazyLoaded = $this->fastLoaded = false;
1247
-		$this->lazyCache = $this->fastCache = $this->valueTypes = $this->configLexiconDetails = [];
1248
-		$this->localCache?->remove(self::LOCAL_CACHE_KEY);
1249
-
1250
-		if (!$reload) {
1251
-			return;
1252
-		}
1253
-
1254
-		$this->loadConfig(lazy: true);
1255
-	}
1256
-
1257
-
1258
-	/**
1259
-	 * For debug purpose.
1260
-	 * Returns the cached data.
1261
-	 *
1262
-	 * @return array
1263
-	 * @since 29.0.0
1264
-	 * @internal
1265
-	 */
1266
-	public function statusCache(): array {
1267
-		return [
1268
-			'fastLoaded' => $this->fastLoaded,
1269
-			'fastCache' => $this->fastCache,
1270
-			'lazyLoaded' => $this->lazyLoaded,
1271
-			'lazyCache' => $this->lazyCache,
1272
-		];
1273
-	}
1274
-
1275
-	/**
1276
-	 * @param int $needle bitflag to search
1277
-	 * @param int $type known value
1278
-	 *
1279
-	 * @return bool TRUE if bitflag $needle is set in $type
1280
-	 */
1281
-	private function isTyped(int $needle, int $type): bool {
1282
-		return (($needle & $type) !== 0);
1283
-	}
1284
-
1285
-	/**
1286
-	 * Confirm the string set for app and key fit the database description
1287
-	 *
1288
-	 * @param string $app assert $app fit in database
1289
-	 * @param string $configKey assert config key fit in database
1290
-	 * @param bool $allowEmptyApp $app can be empty string
1291
-	 * @param int $valueType assert value type is only one type
1292
-	 *
1293
-	 * @throws InvalidArgumentException
1294
-	 */
1295
-	private function assertParams(string $app = '', string $configKey = '', bool $allowEmptyApp = false, int $valueType = -1): void {
1296
-		if (!$allowEmptyApp && $app === '') {
1297
-			throw new InvalidArgumentException('app cannot be an empty string');
1298
-		}
1299
-		if (strlen($app) > self::APP_MAX_LENGTH) {
1300
-			throw new InvalidArgumentException(
1301
-				'Value (' . $app . ') for app is too long (' . self::APP_MAX_LENGTH . ')'
1302
-			);
1303
-		}
1304
-		if (strlen($configKey) > self::KEY_MAX_LENGTH) {
1305
-			throw new InvalidArgumentException('Value (' . $configKey . ') for key is too long (' . self::KEY_MAX_LENGTH . ')');
1306
-		}
1307
-		if ($valueType > -1) {
1308
-			$valueType &= ~self::VALUE_SENSITIVE;
1309
-			if (!in_array($valueType, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
1310
-				throw new InvalidArgumentException('Unknown value type');
1311
-			}
1312
-		}
1313
-	}
1314
-
1315
-	/**
1316
-	 * Load normal config or config set as lazy loaded
1317
-	 *
1318
-	 * @param bool $lazy set to TRUE to also load config values set as lazy loaded
1319
-	 */
1320
-	private function loadConfig(?string $app = null, bool $lazy = false): void {
1321
-		if ($this->isLoaded($lazy)) {
1322
-			return;
1323
-		}
1324
-
1325
-		// if lazy is null or true, we debug log
1326
-		if ($lazy === true && $app !== null) {
1327
-			$exception = new \RuntimeException('The loading of lazy AppConfig values have been triggered by app "' . $app . '"');
1328
-			$this->logger->debug($exception->getMessage(), ['exception' => $exception, 'app' => $app]);
1329
-		}
1330
-
1331
-		$loadLazyOnly = $lazy && $this->isLoaded();
1332
-
1333
-		/** @var array<mixed> */
1334
-		$cacheContent = $this->localCache?->get(self::LOCAL_CACHE_KEY) ?? [];
1335
-		$includesLazyValues = !empty($cacheContent) && !empty($cacheContent['lazyCache']);
1336
-		if (!empty($cacheContent) && (!$lazy || $includesLazyValues)) {
1337
-			$this->valueTypes = $cacheContent['valueTypes'];
1338
-			$this->fastCache = $cacheContent['fastCache'];
1339
-			$this->fastLoaded = !empty($this->fastCache);
1340
-			if ($includesLazyValues) {
1341
-				$this->lazyCache = $cacheContent['lazyCache'];
1342
-				$this->lazyLoaded = !empty($this->lazyCache);
1343
-			}
1344
-			return;
1345
-		}
1346
-
1347
-		// Otherwise no cache available and we need to fetch from database
1348
-		$qb = $this->connection->getQueryBuilder();
1349
-		$qb->from('appconfig')
1350
-			->select('appid', 'configkey', 'configvalue', 'type');
1351
-
1352
-		if ($lazy === false) {
1353
-			$qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
1354
-		} else {
1355
-			if ($loadLazyOnly) {
1356
-				$qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)));
1357
-			}
1358
-			$qb->addSelect('lazy');
1359
-		}
1360
-
1361
-		$result = $qb->executeQuery();
1362
-		$rows = $result->fetchAll();
1363
-		foreach ($rows as $row) {
1364
-			// most of the time, 'lazy' is not in the select because its value is already known
1365
-			if ($lazy && ((int)$row['lazy']) === 1) {
1366
-				$this->lazyCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
1367
-			} else {
1368
-				$this->fastCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
1369
-			}
1370
-			$this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0);
1371
-		}
1372
-
1373
-		$result->closeCursor();
1374
-		$this->localCache?->set(
1375
-			self::LOCAL_CACHE_KEY,
1376
-			[
1377
-				'fastCache' => $this->fastCache,
1378
-				'lazyCache' => $this->lazyCache,
1379
-				'valueTypes' => $this->valueTypes,
1380
-			],
1381
-			self::LOCAL_CACHE_TTL,
1382
-		);
1383
-
1384
-		$this->fastLoaded = true;
1385
-		$this->lazyLoaded = $lazy;
1386
-	}
1387
-
1388
-	/**
1389
-	 * @param bool $lazy - If set to true then also check if lazy values are loaded
1390
-	 */
1391
-	private function isLoaded(bool $lazy = false): bool {
1392
-		return $this->fastLoaded && (!$lazy || $this->lazyLoaded);
1393
-	}
1394
-
1395
-	/**
1396
-	 * Gets the config value
1397
-	 *
1398
-	 * @param string $app app
1399
-	 * @param string $key key
1400
-	 * @param string $default - Default value if the key does not exist
1401
-	 *
1402
-	 * @return string the value or $default
1403
-	 * @deprecated 29.0.0 use getValue*()
1404
-	 *
1405
-	 * This function gets a value from the appconfig table. If the key does
1406
-	 * not exist the default value will be returned
1407
-	 */
1408
-	public function getValue($app, $key, $default = '') {
1409
-		$this->loadConfig($app);
1410
-		$this->matchAndApplyLexiconDefinition($app, $key);
1411
-
1412
-		return $this->fastCache[$app][$key] ?? $default;
1413
-	}
1414
-
1415
-	/**
1416
-	 * Sets a value. If the key did not exist before it will be created.
1417
-	 *
1418
-	 * @param string $app app
1419
-	 * @param string $key key
1420
-	 * @param string|float|int $value value
1421
-	 *
1422
-	 * @return bool True if the value was inserted or updated, false if the value was the same
1423
-	 * @throws AppConfigTypeConflictException
1424
-	 * @throws AppConfigUnknownKeyException
1425
-	 * @deprecated 29.0.0
1426
-	 */
1427
-	public function setValue($app, $key, $value) {
1428
-		/**
1429
-		 * TODO: would it be overkill, or decently improve performance, to catch
1430
-		 * call to this method with $key='enabled' and 'hide' config value related
1431
-		 * to $app when the app is disabled (by modifying entry in database: lazy=lazy+2)
1432
-		 * or enabled (lazy=lazy-2)
1433
-		 *
1434
-		 * this solution would remove the loading of config values from disabled app
1435
-		 * unless calling the method.
1436
-		 */
1437
-		return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED);
1438
-	}
1439
-
1440
-
1441
-	/**
1442
-	 * get multiple values, either the app or key can be used as wildcard by setting it to false
1443
-	 *
1444
-	 * @param string|false $app
1445
-	 * @param string|false $key
1446
-	 *
1447
-	 * @return array|false
1448
-	 * @deprecated 29.0.0 use {@see getAllValues()}
1449
-	 */
1450
-	public function getValues($app, $key) {
1451
-		if (($app !== false) === ($key !== false)) {
1452
-			return false;
1453
-		}
1454
-
1455
-		$key = ($key === false) ? '' : $key;
1456
-		if (!$app) {
1457
-			return $this->searchValues($key, false, self::VALUE_MIXED);
1458
-		} else {
1459
-			return $this->getAllValues($app, $key);
1460
-		}
1461
-	}
1462
-
1463
-	/**
1464
-	 * get all values of the app or and filters out sensitive data
1465
-	 *
1466
-	 * @param string $app
1467
-	 *
1468
-	 * @return array
1469
-	 * @deprecated 29.0.0 use {@see getAllValues()}
1470
-	 */
1471
-	public function getFilteredValues($app) {
1472
-		return $this->getAllValues($app, filtered: true);
1473
-	}
1474
-
1475
-
1476
-	/**
1477
-	 * **Warning:** avoid default NULL value for $lazy as this will
1478
-	 * load all lazy values from the database
1479
-	 *
1480
-	 * @param string $app
1481
-	 * @param array<string, string> $values ['key' => 'value']
1482
-	 * @param bool|null $lazy
1483
-	 *
1484
-	 * @return array<string, string|int|float|bool|array>
1485
-	 */
1486
-	private function formatAppValues(string $app, array $values, ?bool $lazy = null): array {
1487
-		foreach ($values as $key => $value) {
1488
-			try {
1489
-				$type = $this->getValueType($app, $key, $lazy);
1490
-			} catch (AppConfigUnknownKeyException) {
1491
-				continue;
1492
-			}
1493
-
1494
-			$values[$key] = $this->convertTypedValue($value, $type);
1495
-		}
1496
-
1497
-		return $values;
1498
-	}
1499
-
1500
-	/**
1501
-	 * convert string value to the expected type
1502
-	 *
1503
-	 * @param string $value
1504
-	 * @param int $type
1505
-	 *
1506
-	 * @return string|int|float|bool|array
1507
-	 */
1508
-	private function convertTypedValue(string $value, int $type): string|int|float|bool|array {
1509
-		switch ($type) {
1510
-			case self::VALUE_INT:
1511
-				return (int)$value;
1512
-			case self::VALUE_FLOAT:
1513
-				return (float)$value;
1514
-			case self::VALUE_BOOL:
1515
-				return in_array(strtolower($value), ['1', 'true', 'yes', 'on']);
1516
-			case self::VALUE_ARRAY:
1517
-				try {
1518
-					return json_decode($value, true, flags: JSON_THROW_ON_ERROR);
1519
-				} catch (JsonException $e) {
1520
-					// ignoreable
1521
-				}
1522
-				break;
1523
-		}
1524
-		return $value;
1525
-	}
1526
-
1527
-	/**
1528
-	 * @param string $app
1529
-	 *
1530
-	 * @return string[]
1531
-	 * @deprecated 29.0.0 data sensitivity should be set when calling setValue*()
1532
-	 */
1533
-	private function getSensitiveKeys(string $app): array {
1534
-		$sensitiveValues = [
1535
-			'circles' => [
1536
-				'/^key_pairs$/',
1537
-				'/^local_gskey$/',
1538
-			],
1539
-			'call_summary_bot' => [
1540
-				'/^secret_(.*)$/',
1541
-			],
1542
-			'external' => [
1543
-				'/^sites$/',
1544
-				'/^jwt_token_privkey_(.*)$/',
1545
-			],
1546
-			'globalsiteselector' => [
1547
-				'/^gss\.jwt\.key$/',
1548
-			],
1549
-			'gpgmailer' => [
1550
-				'/^GpgServerKey$/',
1551
-			],
1552
-			'integration_discourse' => [
1553
-				'/^private_key$/',
1554
-				'/^public_key$/',
1555
-			],
1556
-			'integration_dropbox' => [
1557
-				'/^client_id$/',
1558
-				'/^client_secret$/',
1559
-			],
1560
-			'integration_github' => [
1561
-				'/^client_id$/',
1562
-				'/^client_secret$/',
1563
-			],
1564
-			'integration_gitlab' => [
1565
-				'/^client_id$/',
1566
-				'/^client_secret$/',
1567
-				'/^oauth_instance_url$/',
1568
-			],
1569
-			'integration_google' => [
1570
-				'/^client_id$/',
1571
-				'/^client_secret$/',
1572
-			],
1573
-			'integration_jira' => [
1574
-				'/^client_id$/',
1575
-				'/^client_secret$/',
1576
-				'/^forced_instance_url$/',
1577
-			],
1578
-			'integration_onedrive' => [
1579
-				'/^client_id$/',
1580
-				'/^client_secret$/',
1581
-			],
1582
-			'integration_openproject' => [
1583
-				'/^client_id$/',
1584
-				'/^client_secret$/',
1585
-				'/^oauth_instance_url$/',
1586
-			],
1587
-			'integration_reddit' => [
1588
-				'/^client_id$/',
1589
-				'/^client_secret$/',
1590
-			],
1591
-			'integration_suitecrm' => [
1592
-				'/^client_id$/',
1593
-				'/^client_secret$/',
1594
-				'/^oauth_instance_url$/',
1595
-			],
1596
-			'integration_twitter' => [
1597
-				'/^consumer_key$/',
1598
-				'/^consumer_secret$/',
1599
-				'/^followed_user$/',
1600
-			],
1601
-			'integration_zammad' => [
1602
-				'/^client_id$/',
1603
-				'/^client_secret$/',
1604
-				'/^oauth_instance_url$/',
1605
-			],
1606
-			'maps' => [
1607
-				'/^mapboxAPIKEY$/',
1608
-			],
1609
-			'notify_push' => [
1610
-				'/^cookie$/',
1611
-			],
1612
-			'onlyoffice' => [
1613
-				'/^jwt_secret$/',
1614
-			],
1615
-			'passwords' => [
1616
-				'/^SSEv1ServerKey$/',
1617
-			],
1618
-			'serverinfo' => [
1619
-				'/^token$/',
1620
-			],
1621
-			'spreed' => [
1622
-				'/^bridge_bot_password$/',
1623
-				'/^hosted-signaling-server-(.*)$/',
1624
-				'/^recording_servers$/',
1625
-				'/^signaling_servers$/',
1626
-				'/^signaling_ticket_secret$/',
1627
-				'/^signaling_token_privkey_(.*)$/',
1628
-				'/^signaling_token_pubkey_(.*)$/',
1629
-				'/^sip_bridge_dialin_info$/',
1630
-				'/^sip_bridge_shared_secret$/',
1631
-				'/^stun_servers$/',
1632
-				'/^turn_servers$/',
1633
-				'/^turn_server_secret$/',
1634
-			],
1635
-			'support' => [
1636
-				'/^last_response$/',
1637
-				'/^potential_subscription_key$/',
1638
-				'/^subscription_key$/',
1639
-			],
1640
-			'theming' => [
1641
-				'/^imprintUrl$/',
1642
-				'/^privacyUrl$/',
1643
-				'/^slogan$/',
1644
-				'/^url$/',
1645
-			],
1646
-			'twofactor_gateway' => [
1647
-				'/^.*token$/',
1648
-			],
1649
-			'user_ldap' => [
1650
-				'/^(s..)?ldap_agent_password$/',
1651
-			],
1652
-			'user_saml' => [
1653
-				'/^idp-x509cert$/',
1654
-			],
1655
-			'whiteboard' => [
1656
-				'/^jwt_secret_key$/',
1657
-			],
1658
-		];
1659
-
1660
-		return $sensitiveValues[$app] ?? [];
1661
-	}
1662
-
1663
-	/**
1664
-	 * Clear all the cached app config values
1665
-	 * New cache will be generated next time a config value is retrieved
1666
-	 *
1667
-	 * @deprecated 29.0.0 use {@see clearCache()}
1668
-	 */
1669
-	public function clearCachedConfig(): void {
1670
-		$this->clearCache();
1671
-	}
1672
-
1673
-	/**
1674
-	 * Match and apply current use of config values with defined lexicon.
1675
-	 * Set $lazy to NULL only if only interested into checking that $key is alias.
1676
-	 *
1677
-	 * @throws AppConfigUnknownKeyException
1678
-	 * @throws AppConfigTypeConflictException
1679
-	 * @return bool TRUE if everything is fine compared to lexicon or lexicon does not exist
1680
-	 */
1681
-	private function matchAndApplyLexiconDefinition(
1682
-		string $app,
1683
-		string &$key,
1684
-		?bool &$lazy = null,
1685
-		int &$type = self::VALUE_MIXED,
1686
-		?string &$default = null,
1687
-		?Entry &$lexiconEntry = null,
1688
-	): bool {
1689
-		if (in_array($key,
1690
-			[
1691
-				'enabled',
1692
-				'installed_version',
1693
-				'types',
1694
-			])) {
1695
-			return true; // we don't break stuff for this list of config keys.
1696
-		}
1697
-		$configDetails = $this->getConfigDetailsFromLexicon($app);
1698
-		if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
1699
-			// in case '$rename' is set in ConfigLexiconEntry, we use the new config key
1700
-			$key = $configDetails['aliases'][$key];
1701
-		}
1702
-
1703
-		if (!array_key_exists($key, $configDetails['entries'])) {
1704
-			return $this->applyLexiconStrictness($configDetails['strictness'], $app . '/' . $key);
1705
-		}
1706
-
1707
-		// if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon
1708
-		if ($lazy === null) {
1709
-			return true;
1710
-		}
1711
-
1712
-		/** @var Entry $lexiconEntry */
1713
-		$lexiconEntry = $configDetails['entries'][$key];
1714
-		$type &= ~self::VALUE_SENSITIVE;
1715
-
1716
-		$appConfigValueType = $lexiconEntry->getValueType()->toAppConfigFlag();
1717
-		if ($type === self::VALUE_MIXED) {
1718
-			$type = $appConfigValueType; // we overwrite if value was requested as mixed
1719
-		} elseif ($appConfigValueType !== $type) {
1720
-			throw new AppConfigTypeConflictException('The app config key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon');
1721
-		}
1722
-
1723
-		$lazy = $lexiconEntry->isLazy();
1724
-		// only look for default if needed, default from Lexicon got priority
1725
-		if ($default !== null) {
1726
-			$default = $lexiconEntry->getDefault($this->presetManager->getLexiconPreset()) ?? $default;
1727
-		}
1728
-
1729
-		if ($lexiconEntry->isFlagged(self::FLAG_SENSITIVE)) {
1730
-			$type |= self::VALUE_SENSITIVE;
1731
-		}
1732
-		if ($lexiconEntry->isDeprecated()) {
1733
-			$this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.');
1734
-		}
1735
-
1736
-		return true;
1737
-	}
1738
-
1739
-	/**
1740
-	 * manage ConfigLexicon behavior based on strictness set in IConfigLexicon
1741
-	 *
1742
-	 * @param Strictness|null $strictness
1743
-	 * @param string $line
1744
-	 *
1745
-	 * @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed
1746
-	 * @throws AppConfigUnknownKeyException if strictness implies exception
1747
-	 * @see \OCP\Config\Lexicon\ILexicon::getStrictness()
1748
-	 */
1749
-	private function applyLexiconStrictness(?Strictness $strictness, string $configAppKey): bool {
1750
-		if ($strictness === null) {
1751
-			return true;
1752
-		}
1753
-
1754
-		$line = 'The app config key ' . $configAppKey . ' is not defined in the config lexicon';
1755
-		switch ($strictness) {
1756
-			case Strictness::IGNORE:
1757
-				return true;
1758
-			case Strictness::NOTICE:
1759
-				if (!in_array($configAppKey, $this->strictnessApplied, true)) {
1760
-					$this->strictnessApplied[] = $configAppKey;
1761
-					$this->logger->notice($line);
1762
-				}
1763
-				return true;
1764
-			case Strictness::WARNING:
1765
-				if (!in_array($configAppKey, $this->strictnessApplied, true)) {
1766
-					$this->strictnessApplied[] = $configAppKey;
1767
-					$this->logger->warning($line);
1768
-				}
1769
-				return false;
1770
-		}
1771
-
1772
-		throw new AppConfigUnknownKeyException($line);
1773
-	}
1774
-
1775
-	/**
1776
-	 * extract details from registered $appId's config lexicon
1777
-	 *
1778
-	 * @param string $appId
1779
-	 * @internal
1780
-	 *
1781
-	 * @return array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}
1782
-	 */
1783
-	public function getConfigDetailsFromLexicon(string $appId): array {
1784
-		if (!array_key_exists($appId, $this->configLexiconDetails)) {
1785
-			$entries = $aliases = [];
1786
-			$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
1787
-			$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
1788
-			foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
1789
-				$entries[$configEntry->getKey()] = $configEntry;
1790
-				$newName = $configEntry->getRename();
1791
-				if ($newName !== null) {
1792
-					$aliases[$newName] = $configEntry->getKey();
1793
-				}
1794
-			}
1795
-
1796
-			$this->configLexiconDetails[$appId] = [
1797
-				'entries' => $entries,
1798
-				'aliases' => $aliases,
1799
-				'strictness' => $configLexicon?->getStrictness() ?? Strictness::IGNORE
1800
-			];
1801
-		}
1802
-
1803
-		return $this->configLexiconDetails[$appId];
1804
-	}
1805
-
1806
-	/**
1807
-	 * get Lexicon Entry using appId and config key entry
1808
-	 *
1809
-	 * @return Entry|null NULL if entry does not exist in app's Lexicon
1810
-	 * @internal
1811
-	 */
1812
-	public function getLexiconEntry(string $appId, string $key): ?Entry {
1813
-		return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null;
1814
-	}
1815
-
1816
-	/**
1817
-	 * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class
1818
-	 *
1819
-	 * @internal
1820
-	 */
1821
-	public function ignoreLexiconAliases(bool $ignore): void {
1822
-		$this->ignoreLexiconAliases = $ignore;
1823
-	}
1824
-
1825
-	/**
1826
-	 * Returns the installed versions of all apps
1827
-	 *
1828
-	 * @return array<string, string>
1829
-	 */
1830
-	public function getAppInstalledVersions(bool $onlyEnabled = false): array {
1831
-		if ($this->appVersionsCache === null) {
1832
-			/** @var array<string, string> */
1833
-			$this->appVersionsCache = $this->searchValues('installed_version', false, IAppConfig::VALUE_STRING);
1834
-		}
1835
-		if ($onlyEnabled) {
1836
-			return array_filter(
1837
-				$this->appVersionsCache,
1838
-				fn (string $app): bool => $this->getValueString($app, 'enabled', 'no') !== 'no',
1839
-				ARRAY_FILTER_USE_KEY
1840
-			);
1841
-		}
1842
-		return $this->appVersionsCache;
1843
-	}
1844
-
1845
-	private function clearLocalCache(): void {
1846
-		$this->localCache?->remove(self::LOCAL_CACHE_KEY);
1847
-	}
54
+    private const APP_MAX_LENGTH = 32;
55
+    private const KEY_MAX_LENGTH = 64;
56
+    private const ENCRYPTION_PREFIX = '$AppConfigEncryption$';
57
+    private const ENCRYPTION_PREFIX_LENGTH = 21; // strlen(self::ENCRYPTION_PREFIX)
58
+    private const LOCAL_CACHE_KEY = 'OC\\AppConfig';
59
+    private const LOCAL_CACHE_TTL = 3;
60
+
61
+    /** @var array<string, array<string, string>> ['app_id' => ['config_key' => 'config_value']] */
62
+    private array $fastCache = [];   // cache for normal config keys
63
+    /** @var array<string, array<string, string>> ['app_id' => ['config_key' => 'config_value']] */
64
+    private array $lazyCache = [];   // cache for lazy config keys
65
+    /** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */
66
+    private array $valueTypes = [];  // type for all config values
67
+    private bool $fastLoaded = false;
68
+    private bool $lazyLoaded = false;
69
+    /** @var array<string, array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
70
+    private array $configLexiconDetails = [];
71
+    private bool $ignoreLexiconAliases = false;
72
+    private array $strictnessApplied = [];
73
+
74
+    /** @var ?array<string, string> */
75
+    private ?array $appVersionsCache = null;
76
+    private ?ICache $localCache = null;
77
+
78
+    public function __construct(
79
+        protected IDBConnection $connection,
80
+        protected IConfig $config,
81
+        private readonly ConfigManager $configManager,
82
+        private readonly PresetManager $presetManager,
83
+        protected LoggerInterface $logger,
84
+        protected ICrypto $crypto,
85
+        readonly CacheFactory $cacheFactory,
86
+    ) {
87
+        if ($config->getSystemValueBool('cache_app_config', true) && $cacheFactory->isLocalCacheAvailable()) {
88
+            $cacheFactory->withServerVersionPrefix(function (ICacheFactory $factory) {
89
+                $this->localCache = $factory->createLocal();
90
+            });
91
+        }
92
+    }
93
+
94
+    /**
95
+     * @inheritDoc
96
+     *
97
+     * @return list<string> list of app ids
98
+     * @since 7.0.0
99
+     */
100
+    public function getApps(): array {
101
+        $this->loadConfig(lazy: true);
102
+        $apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache));
103
+        sort($apps);
104
+
105
+        return array_values(array_unique($apps));
106
+    }
107
+
108
+    /**
109
+     * @inheritDoc
110
+     *
111
+     * @param string $app id of the app
112
+     * @return list<string> list of stored config keys
113
+     * @see searchKeys to not load lazy config keys
114
+     *
115
+     * @since 29.0.0
116
+     */
117
+    public function getKeys(string $app): array {
118
+        $this->assertParams($app);
119
+        $this->loadConfig($app, true);
120
+        $keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? []));
121
+        sort($keys);
122
+
123
+        return array_values(array_unique($keys));
124
+    }
125
+
126
+    /**
127
+     * @inheritDoc
128
+     *
129
+     * @param string $app id of the app
130
+     * @param string $prefix returns only keys starting with this value
131
+     * @param bool $lazy TRUE to search in lazy config keys
132
+     * @return list<string> list of stored config keys
133
+     * @since 32.0.0
134
+     */
135
+    public function searchKeys(string $app, string $prefix = '', bool $lazy = false): array {
136
+        $this->assertParams($app);
137
+        $this->loadConfig($app, $lazy);
138
+        if ($lazy) {
139
+            $keys = array_keys($this->lazyCache[$app] ?? []);
140
+        } else {
141
+            $keys = array_keys($this->fastCache[$app] ?? []);
142
+        }
143
+
144
+        if ($prefix !== '') {
145
+            $keys = array_filter($keys, static fn (string $key): bool => str_starts_with($key, $prefix));
146
+        }
147
+
148
+        sort($keys);
149
+        return array_values(array_unique($keys));
150
+    }
151
+
152
+    /**
153
+     * @inheritDoc
154
+     *
155
+     * @param string $app id of the app
156
+     * @param string $key config key
157
+     * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
158
+     *
159
+     * @return bool TRUE if key exists
160
+     * @since 7.0.0
161
+     * @since 29.0.0 Added the $lazy argument
162
+     */
163
+    public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
164
+        $this->assertParams($app, $key);
165
+        $this->loadConfig($app, $lazy ?? true);
166
+        $this->matchAndApplyLexiconDefinition($app, $key);
167
+
168
+        $hasLazy = isset($this->lazyCache[$app][$key]);
169
+        $hasFast = isset($this->fastCache[$app][$key]);
170
+        if ($lazy === null) {
171
+            return $hasLazy || $hasFast;
172
+        } else {
173
+            return $lazy ? $hasLazy : $hasFast;
174
+        }
175
+    }
176
+
177
+    /**
178
+     * @param string $app id of the app
179
+     * @param string $key config key
180
+     * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
181
+     *
182
+     * @return bool
183
+     * @throws AppConfigUnknownKeyException if config key is not known
184
+     * @since 29.0.0
185
+     */
186
+    public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
187
+        $this->assertParams($app, $key);
188
+        $this->loadConfig(null, $lazy ?? true);
189
+        $this->matchAndApplyLexiconDefinition($app, $key);
190
+
191
+        if (!isset($this->valueTypes[$app][$key])) {
192
+            throw new AppConfigUnknownKeyException('unknown config key');
193
+        }
194
+
195
+        return $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key]);
196
+    }
197
+
198
+    /**
199
+     * @inheritDoc
200
+     *
201
+     * @param string $app if of the app
202
+     * @param string $key config key
203
+     *
204
+     * @return bool TRUE if config is lazy loaded
205
+     * @throws AppConfigUnknownKeyException if config key is not known
206
+     * @see IAppConfig for details about lazy loading
207
+     * @since 29.0.0
208
+     */
209
+    public function isLazy(string $app, string $key): bool {
210
+        $this->assertParams($app, $key);
211
+        $this->matchAndApplyLexiconDefinition($app, $key);
212
+
213
+        // there is a huge probability the non-lazy config are already loaded
214
+        if ($this->hasKey($app, $key, false)) {
215
+            return false;
216
+        }
217
+
218
+        // key not found, we search in the lazy config
219
+        if ($this->hasKey($app, $key, true)) {
220
+            return true;
221
+        }
222
+
223
+        throw new AppConfigUnknownKeyException('unknown config key');
224
+    }
225
+
226
+
227
+    /**
228
+     * @inheritDoc
229
+     *
230
+     * @param string $app id of the app
231
+     * @param string $prefix config keys prefix to search
232
+     * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE}
233
+     *
234
+     * @return array<string, string|int|float|bool|array> [configKey => configValue]
235
+     * @since 29.0.0
236
+     */
237
+    public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array {
238
+        $this->assertParams($app, $prefix);
239
+        // if we want to filter values, we need to get sensitivity
240
+        $this->loadConfig($app, true);
241
+        // array_merge() will remove numeric keys (here config keys), so addition arrays instead
242
+        $values = $this->formatAppValues($app, ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? []));
243
+        $values = array_filter(
244
+            $values,
245
+            function (string $key) use ($prefix): bool {
246
+                return str_starts_with($key, $prefix); // filter values based on $prefix
247
+            }, ARRAY_FILTER_USE_KEY
248
+        );
249
+
250
+        if (!$filtered) {
251
+            return $values;
252
+        }
253
+
254
+        /**
255
+         * Using the old (deprecated) list of sensitive values.
256
+         */
257
+        foreach ($this->getSensitiveKeys($app) as $sensitiveKeyExp) {
258
+            $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values));
259
+            foreach ($sensitiveKeys as $sensitiveKey) {
260
+                $this->valueTypes[$app][$sensitiveKey] = ($this->valueTypes[$app][$sensitiveKey] ?? 0) | self::VALUE_SENSITIVE;
261
+            }
262
+        }
263
+
264
+        $result = [];
265
+        foreach ($values as $key => $value) {
266
+            $result[$key] = $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key] ?? 0) ? IConfig::SENSITIVE_VALUE : $value;
267
+        }
268
+
269
+        return $result;
270
+    }
271
+
272
+    /**
273
+     * @inheritDoc
274
+     *
275
+     * @param string $key config key
276
+     * @param bool $lazy search within lazy loaded config
277
+     * @param int|null $typedAs enforce type for the returned values ({@see self::VALUE_STRING} and others)
278
+     *
279
+     * @return array<string, string|int|float|bool|array> [appId => configValue]
280
+     * @since 29.0.0
281
+     */
282
+    public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array {
283
+        $this->assertParams('', $key, true);
284
+        $this->loadConfig(null, $lazy);
285
+
286
+        /** @var array<array-key, array<array-key, mixed>> $cache */
287
+        if ($lazy) {
288
+            $cache = $this->lazyCache;
289
+        } else {
290
+            $cache = $this->fastCache;
291
+        }
292
+
293
+        $values = [];
294
+        foreach (array_keys($cache) as $app) {
295
+            if (isset($cache[$app][$key])) {
296
+                $values[$app] = $this->convertTypedValue($cache[$app][$key], $typedAs ?? $this->getValueType((string)$app, $key, $lazy));
297
+            }
298
+        }
299
+
300
+        return $values;
301
+    }
302
+
303
+
304
+    /**
305
+     * Get the config value as string.
306
+     * If the value does not exist the given default will be returned.
307
+     *
308
+     * Set lazy to `null` to ignore it and get the value from either source.
309
+     *
310
+     * **WARNING:** Method is internal and **SHOULD** not be used, as it is better to get the value with a type.
311
+     *
312
+     * @param string $app id of the app
313
+     * @param string $key config key
314
+     * @param string $default config value
315
+     * @param null|bool $lazy get config as lazy loaded or not. can be NULL
316
+     *
317
+     * @return string the value or $default
318
+     * @internal
319
+     * @since 29.0.0
320
+     * @see IAppConfig for explanation about lazy loading
321
+     * @see getValueString()
322
+     * @see getValueInt()
323
+     * @see getValueFloat()
324
+     * @see getValueBool()
325
+     * @see getValueArray()
326
+     */
327
+    public function getValueMixed(
328
+        string $app,
329
+        string $key,
330
+        string $default = '',
331
+        ?bool $lazy = false,
332
+    ): string {
333
+        try {
334
+            $lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy;
335
+        } catch (AppConfigUnknownKeyException) {
336
+            return $default;
337
+        }
338
+
339
+        return $this->getTypedValue(
340
+            $app,
341
+            $key,
342
+            $default,
343
+            $lazy,
344
+            self::VALUE_MIXED
345
+        );
346
+    }
347
+
348
+    /**
349
+     * @inheritDoc
350
+     *
351
+     * @param string $app id of the app
352
+     * @param string $key config key
353
+     * @param string $default default value
354
+     * @param bool $lazy search within lazy loaded config
355
+     *
356
+     * @return string stored config value or $default if not set in database
357
+     * @throws InvalidArgumentException if one of the argument format is invalid
358
+     * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
359
+     * @since 29.0.0
360
+     * @see IAppConfig for explanation about lazy loading
361
+     */
362
+    public function getValueString(
363
+        string $app,
364
+        string $key,
365
+        string $default = '',
366
+        bool $lazy = false,
367
+    ): string {
368
+        return $this->getTypedValue($app, $key, $default, $lazy, self::VALUE_STRING);
369
+    }
370
+
371
+    /**
372
+     * @inheritDoc
373
+     *
374
+     * @param string $app id of the app
375
+     * @param string $key config key
376
+     * @param int $default default value
377
+     * @param bool $lazy search within lazy loaded config
378
+     *
379
+     * @return int stored config value or $default if not set in database
380
+     * @throws InvalidArgumentException if one of the argument format is invalid
381
+     * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
382
+     * @since 29.0.0
383
+     * @see IAppConfig for explanation about lazy loading
384
+     */
385
+    public function getValueInt(
386
+        string $app,
387
+        string $key,
388
+        int $default = 0,
389
+        bool $lazy = false,
390
+    ): int {
391
+        return (int)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_INT);
392
+    }
393
+
394
+    /**
395
+     * @inheritDoc
396
+     *
397
+     * @param string $app id of the app
398
+     * @param string $key config key
399
+     * @param float $default default value
400
+     * @param bool $lazy search within lazy loaded config
401
+     *
402
+     * @return float stored config value or $default if not set in database
403
+     * @throws InvalidArgumentException if one of the argument format is invalid
404
+     * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
405
+     * @since 29.0.0
406
+     * @see IAppConfig for explanation about lazy loading
407
+     */
408
+    public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float {
409
+        return (float)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_FLOAT);
410
+    }
411
+
412
+    /**
413
+     * @inheritDoc
414
+     *
415
+     * @param string $app id of the app
416
+     * @param string $key config key
417
+     * @param bool $default default value
418
+     * @param bool $lazy search within lazy loaded config
419
+     *
420
+     * @return bool stored config value or $default if not set in database
421
+     * @throws InvalidArgumentException if one of the argument format is invalid
422
+     * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
423
+     * @since 29.0.0
424
+     * @see IAppConfig for explanation about lazy loading
425
+     */
426
+    public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool {
427
+        $b = strtolower($this->getTypedValue($app, $key, $default ? 'true' : 'false', $lazy, self::VALUE_BOOL));
428
+        return in_array($b, ['1', 'true', 'yes', 'on']);
429
+    }
430
+
431
+    /**
432
+     * @inheritDoc
433
+     *
434
+     * @param string $app id of the app
435
+     * @param string $key config key
436
+     * @param array $default default value
437
+     * @param bool $lazy search within lazy loaded config
438
+     *
439
+     * @return array stored config value or $default if not set in database
440
+     * @throws InvalidArgumentException if one of the argument format is invalid
441
+     * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
442
+     * @since 29.0.0
443
+     * @see IAppConfig for explanation about lazy loading
444
+     */
445
+    public function getValueArray(
446
+        string $app,
447
+        string $key,
448
+        array $default = [],
449
+        bool $lazy = false,
450
+    ): array {
451
+        try {
452
+            $defaultJson = json_encode($default, JSON_THROW_ON_ERROR);
453
+            $value = json_decode($this->getTypedValue($app, $key, $defaultJson, $lazy, self::VALUE_ARRAY), true, flags: JSON_THROW_ON_ERROR);
454
+
455
+            return is_array($value) ? $value : [];
456
+        } catch (JsonException) {
457
+            return [];
458
+        }
459
+    }
460
+
461
+    /**
462
+     * @param string $app id of the app
463
+     * @param string $key config key
464
+     * @param string $default default value
465
+     * @param bool $lazy search within lazy loaded config
466
+     * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT}{@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
467
+     *
468
+     * @return string
469
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
470
+     * @throws InvalidArgumentException
471
+     */
472
+    private function getTypedValue(
473
+        string $app,
474
+        string $key,
475
+        string $default,
476
+        bool $lazy,
477
+        int $type,
478
+    ): string {
479
+        $this->assertParams($app, $key, valueType: $type);
480
+        $origKey = $key;
481
+        $matched = $this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $default);
482
+        if ($default === null) {
483
+            // there is no logical reason for it to be null
484
+            throw new \Exception('default cannot be null');
485
+        }
486
+
487
+        // returns default if strictness of lexicon is set to WARNING (block and report)
488
+        if (!$matched) {
489
+            return $default;
490
+        }
491
+
492
+        $this->loadConfig($app, $lazy ?? true);
493
+
494
+        /**
495
+         * We ignore check if mixed type is requested.
496
+         * If type of stored value is set as mixed, we don't filter.
497
+         * If type of stored value is defined, we compare with the one requested.
498
+         */
499
+        $knownType = $this->valueTypes[$app][$key] ?? 0;
500
+        if (!$this->isTyped(self::VALUE_MIXED, $type)
501
+            && $knownType > 0
502
+            && !$this->isTyped(self::VALUE_MIXED, $knownType)
503
+            && !$this->isTyped($type, $knownType)) {
504
+            $this->logger->warning('conflict with value type from database', ['app' => $app, 'key' => $key, 'type' => $type, 'knownType' => $knownType]);
505
+            throw new AppConfigTypeConflictException('conflict with value type from database');
506
+        }
507
+
508
+        /**
509
+         * - the pair $app/$key cannot exist in both array,
510
+         * - we should still return an existing non-lazy value even if current method
511
+         *   is called with $lazy is true
512
+         *
513
+         * This way, lazyCache will be empty until the load for lazy config value is requested.
514
+         */
515
+        if (isset($this->lazyCache[$app][$key])) {
516
+            $value = $this->lazyCache[$app][$key];
517
+        } elseif (isset($this->fastCache[$app][$key])) {
518
+            $value = $this->fastCache[$app][$key];
519
+        } else {
520
+            return $default;
521
+        }
522
+
523
+        $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $knownType);
524
+        if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) {
525
+            // Only decrypt values that are stored encrypted
526
+            $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
527
+        }
528
+
529
+        // in case the key was modified while running matchAndApplyLexiconDefinition() we are
530
+        // interested to check options in case a modification of the value is needed
531
+        // ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN
532
+        if ($origKey !== $key && $type === self::VALUE_BOOL) {
533
+            $value = ($this->configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0';
534
+        }
535
+
536
+        return $value;
537
+    }
538
+
539
+    /**
540
+     * @inheritDoc
541
+     *
542
+     * @param string $app id of the app
543
+     * @param string $key config key
544
+     *
545
+     * @return int type of the value
546
+     * @throws AppConfigUnknownKeyException if config key is not known
547
+     * @since 29.0.0
548
+     * @see VALUE_STRING
549
+     * @see VALUE_INT
550
+     * @see VALUE_FLOAT
551
+     * @see VALUE_BOOL
552
+     * @see VALUE_ARRAY
553
+     */
554
+    public function getValueType(string $app, string $key, ?bool $lazy = null): int {
555
+        $type = self::VALUE_MIXED;
556
+        $ignorable = $lazy ?? false;
557
+        $this->matchAndApplyLexiconDefinition($app, $key, $ignorable, $type);
558
+        if ($type !== self::VALUE_MIXED) {
559
+            // a modified $type means config key is set in Lexicon
560
+            return $type;
561
+        }
562
+
563
+        $this->assertParams($app, $key);
564
+        $this->loadConfig($app, $lazy ?? true);
565
+
566
+        if (!isset($this->valueTypes[$app][$key])) {
567
+            throw new AppConfigUnknownKeyException('unknown config key');
568
+        }
569
+
570
+        $type = $this->valueTypes[$app][$key];
571
+        $type &= ~self::VALUE_SENSITIVE;
572
+        return $type;
573
+    }
574
+
575
+
576
+    /**
577
+     * Store a config key and its value in database as VALUE_MIXED
578
+     *
579
+     * **WARNING:** Method is internal and **MUST** not be used as it is best to set a real value type
580
+     *
581
+     * @param string $app id of the app
582
+     * @param string $key config key
583
+     * @param string $value config value
584
+     * @param bool $lazy set config as lazy loaded
585
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
586
+     *
587
+     * @return bool TRUE if value was different, therefor updated in database
588
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED
589
+     * @internal
590
+     * @since 29.0.0
591
+     * @see IAppConfig for explanation about lazy loading
592
+     * @see setValueString()
593
+     * @see setValueInt()
594
+     * @see setValueFloat()
595
+     * @see setValueBool()
596
+     * @see setValueArray()
597
+     */
598
+    public function setValueMixed(
599
+        string $app,
600
+        string $key,
601
+        string $value,
602
+        bool $lazy = false,
603
+        bool $sensitive = false,
604
+    ): bool {
605
+        return $this->setTypedValue(
606
+            $app,
607
+            $key,
608
+            $value,
609
+            $lazy,
610
+            self::VALUE_MIXED | ($sensitive ? self::VALUE_SENSITIVE : 0)
611
+        );
612
+    }
613
+
614
+
615
+    /**
616
+     * @inheritDoc
617
+     *
618
+     * @param string $app id of the app
619
+     * @param string $key config key
620
+     * @param string $value config value
621
+     * @param bool $lazy set config as lazy loaded
622
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
623
+     *
624
+     * @return bool TRUE if value was different, therefor updated in database
625
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
626
+     * @since 29.0.0
627
+     * @see IAppConfig for explanation about lazy loading
628
+     */
629
+    public function setValueString(
630
+        string $app,
631
+        string $key,
632
+        string $value,
633
+        bool $lazy = false,
634
+        bool $sensitive = false,
635
+    ): bool {
636
+        return $this->setTypedValue(
637
+            $app,
638
+            $key,
639
+            $value,
640
+            $lazy,
641
+            self::VALUE_STRING | ($sensitive ? self::VALUE_SENSITIVE : 0)
642
+        );
643
+    }
644
+
645
+    /**
646
+     * @inheritDoc
647
+     *
648
+     * @param string $app id of the app
649
+     * @param string $key config key
650
+     * @param int $value config value
651
+     * @param bool $lazy set config as lazy loaded
652
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
653
+     *
654
+     * @return bool TRUE if value was different, therefor updated in database
655
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
656
+     * @since 29.0.0
657
+     * @see IAppConfig for explanation about lazy loading
658
+     */
659
+    public function setValueInt(
660
+        string $app,
661
+        string $key,
662
+        int $value,
663
+        bool $lazy = false,
664
+        bool $sensitive = false,
665
+    ): bool {
666
+        if ($value > 2000000000) {
667
+            $this->logger->debug('You are trying to store an integer value around/above 2,147,483,647. This is a reminder that reaching this theoretical limit on 32 bits system will throw an exception.');
668
+        }
669
+
670
+        return $this->setTypedValue(
671
+            $app,
672
+            $key,
673
+            (string)$value,
674
+            $lazy,
675
+            self::VALUE_INT | ($sensitive ? self::VALUE_SENSITIVE : 0)
676
+        );
677
+    }
678
+
679
+    /**
680
+     * @inheritDoc
681
+     *
682
+     * @param string $app id of the app
683
+     * @param string $key config key
684
+     * @param float $value config value
685
+     * @param bool $lazy set config as lazy loaded
686
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
687
+     *
688
+     * @return bool TRUE if value was different, therefor updated in database
689
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
690
+     * @since 29.0.0
691
+     * @see IAppConfig for explanation about lazy loading
692
+     */
693
+    public function setValueFloat(
694
+        string $app,
695
+        string $key,
696
+        float $value,
697
+        bool $lazy = false,
698
+        bool $sensitive = false,
699
+    ): bool {
700
+        return $this->setTypedValue(
701
+            $app,
702
+            $key,
703
+            (string)$value,
704
+            $lazy,
705
+            self::VALUE_FLOAT | ($sensitive ? self::VALUE_SENSITIVE : 0)
706
+        );
707
+    }
708
+
709
+    /**
710
+     * @inheritDoc
711
+     *
712
+     * @param string $app id of the app
713
+     * @param string $key config key
714
+     * @param bool $value config value
715
+     * @param bool $lazy set config as lazy loaded
716
+     *
717
+     * @return bool TRUE if value was different, therefor updated in database
718
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
719
+     * @since 29.0.0
720
+     * @see IAppConfig for explanation about lazy loading
721
+     */
722
+    public function setValueBool(
723
+        string $app,
724
+        string $key,
725
+        bool $value,
726
+        bool $lazy = false,
727
+    ): bool {
728
+        return $this->setTypedValue(
729
+            $app,
730
+            $key,
731
+            ($value) ? '1' : '0',
732
+            $lazy,
733
+            self::VALUE_BOOL
734
+        );
735
+    }
736
+
737
+    /**
738
+     * @inheritDoc
739
+     *
740
+     * @param string $app id of the app
741
+     * @param string $key config key
742
+     * @param array $value config value
743
+     * @param bool $lazy set config as lazy loaded
744
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
745
+     *
746
+     * @return bool TRUE if value was different, therefor updated in database
747
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
748
+     * @throws JsonException
749
+     * @since 29.0.0
750
+     * @see IAppConfig for explanation about lazy loading
751
+     */
752
+    public function setValueArray(
753
+        string $app,
754
+        string $key,
755
+        array $value,
756
+        bool $lazy = false,
757
+        bool $sensitive = false,
758
+    ): bool {
759
+        try {
760
+            return $this->setTypedValue(
761
+                $app,
762
+                $key,
763
+                json_encode($value, JSON_THROW_ON_ERROR),
764
+                $lazy,
765
+                self::VALUE_ARRAY | ($sensitive ? self::VALUE_SENSITIVE : 0)
766
+            );
767
+        } catch (JsonException $e) {
768
+            $this->logger->warning('could not setValueArray', ['app' => $app, 'key' => $key, 'exception' => $e]);
769
+            throw $e;
770
+        }
771
+    }
772
+
773
+    /**
774
+     * Store a config key and its value in database
775
+     *
776
+     * If config key is already known with the exact same config value and same sensitive/lazy status, the
777
+     * database is not updated. If config value was previously stored as sensitive, status will not be
778
+     * altered.
779
+     *
780
+     * @param string $app id of the app
781
+     * @param string $key config key
782
+     * @param string $value config value
783
+     * @param bool $lazy config set as lazy loaded
784
+     * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
785
+     *
786
+     * @return bool TRUE if value was updated in database
787
+     * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
788
+     * @see IAppConfig for explanation about lazy loading
789
+     */
790
+    private function setTypedValue(
791
+        string $app,
792
+        string $key,
793
+        string $value,
794
+        bool $lazy,
795
+        int $type,
796
+    ): bool {
797
+        $this->assertParams($app, $key);
798
+        if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type)) {
799
+            return false; // returns false as database is not updated
800
+        }
801
+        $this->loadConfig(null, $lazy ?? true);
802
+
803
+        $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type);
804
+        $inserted = $refreshCache = false;
805
+
806
+        $origValue = $value;
807
+        if ($sensitive || ($this->hasKey($app, $key, $lazy) && $this->isSensitive($app, $key, $lazy))) {
808
+            $value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value);
809
+        }
810
+
811
+        if ($this->hasKey($app, $key, $lazy)) {
812
+            /**
813
+             * no update if key is already known with set lazy status and value is
814
+             * not different, unless sensitivity is switched from false to true.
815
+             */
816
+            if ($origValue === $this->getTypedValue($app, $key, $value, $lazy ?? true, $type)
817
+                && (!$sensitive || $this->isSensitive($app, $key, $lazy))) {
818
+                return false;
819
+            }
820
+        } else {
821
+            /**
822
+             * if key is not known yet, we try to insert.
823
+             * It might fail if the key exists with a different lazy flag.
824
+             */
825
+            try {
826
+                $insert = $this->connection->getQueryBuilder();
827
+                $insert->insert('appconfig')
828
+                    ->setValue('appid', $insert->createNamedParameter($app))
829
+                    ->setValue('lazy', $insert->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT))
830
+                    ->setValue('type', $insert->createNamedParameter($type, IQueryBuilder::PARAM_INT))
831
+                    ->setValue('configkey', $insert->createNamedParameter($key))
832
+                    ->setValue('configvalue', $insert->createNamedParameter($value));
833
+                $insert->executeStatement();
834
+                $inserted = true;
835
+            } catch (DBException $e) {
836
+                if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
837
+                    throw $e; // TODO: throw exception or just log and returns false !?
838
+                }
839
+            }
840
+        }
841
+
842
+        /**
843
+         * We cannot insert a new row, meaning we need to update an already existing one
844
+         */
845
+        if (!$inserted) {
846
+            $currType = $this->valueTypes[$app][$key] ?? 0;
847
+            if ($currType === 0) { // this might happen when switching lazy loading status
848
+                $this->loadConfig(lazy: true);
849
+                $currType = $this->valueTypes[$app][$key] ?? 0;
850
+            }
851
+
852
+            /**
853
+             * This should only happen during the upgrade process from 28 to 29.
854
+             * We only log a warning and set it to VALUE_MIXED.
855
+             */
856
+            if ($currType === 0) {
857
+                $this->logger->warning('Value type is set to zero (0) in database. This is fine only during the upgrade process from 28 to 29.', ['app' => $app, 'key' => $key]);
858
+                $currType = self::VALUE_MIXED;
859
+            }
860
+
861
+            /**
862
+             * we only accept a different type from the one stored in database
863
+             * if the one stored in database is not-defined (VALUE_MIXED)
864
+             */
865
+            if (!$this->isTyped(self::VALUE_MIXED, $currType)
866
+                && ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
867
+                try {
868
+                    $currType = $this->convertTypeToString($currType);
869
+                    $type = $this->convertTypeToString($type);
870
+                } catch (AppConfigIncorrectTypeException) {
871
+                    // can be ignored, this was just needed for a better exception message.
872
+                }
873
+                throw new AppConfigTypeConflictException('conflict between new type (' . $type . ') and old type (' . $currType . ')');
874
+            }
875
+
876
+            // we fix $type if the stored value, or the new value as it might be changed, is set as sensitive
877
+            if ($sensitive || $this->isTyped(self::VALUE_SENSITIVE, $currType)) {
878
+                $type |= self::VALUE_SENSITIVE;
879
+            }
880
+
881
+            try {
882
+                if ($lazy !== $this->isLazy($app, $key)) {
883
+                    $refreshCache = true;
884
+                }
885
+            } catch (AppConfigUnknownKeyException) {
886
+                // pass
887
+            }
888
+
889
+            $update = $this->connection->getQueryBuilder();
890
+            $update->update('appconfig')
891
+                ->set('configvalue', $update->createNamedParameter($value))
892
+                ->set('lazy', $update->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT))
893
+                ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
894
+                ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
895
+                ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
896
+
897
+            $update->executeStatement();
898
+        }
899
+
900
+        if ($refreshCache) {
901
+            $this->clearCache();
902
+            return true;
903
+        }
904
+
905
+        // update local cache
906
+        if ($lazy) {
907
+            $this->lazyCache[$app][$key] = $value;
908
+        } else {
909
+            $this->fastCache[$app][$key] = $value;
910
+        }
911
+        $this->valueTypes[$app][$key] = $type;
912
+        $this->clearLocalCache();
913
+
914
+        return true;
915
+    }
916
+
917
+    /**
918
+     * Change the type of config value.
919
+     *
920
+     * **WARNING:** Method is internal and **MUST** not be used as it may break things.
921
+     *
922
+     * @param string $app id of the app
923
+     * @param string $key config key
924
+     * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
925
+     *
926
+     * @return bool TRUE if database update were necessary
927
+     * @throws AppConfigUnknownKeyException if $key is now known in database
928
+     * @throws AppConfigIncorrectTypeException if $type is not valid
929
+     * @internal
930
+     * @since 29.0.0
931
+     */
932
+    public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
933
+        $this->assertParams($app, $key);
934
+        $this->loadConfig(lazy: true);
935
+        $this->matchAndApplyLexiconDefinition($app, $key);
936
+        $this->isLazy($app, $key); // confirm key exists
937
+
938
+        // type can only be one type
939
+        if (!in_array($type, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
940
+            throw new AppConfigIncorrectTypeException('Unknown value type');
941
+        }
942
+
943
+        $currType = $this->valueTypes[$app][$key];
944
+        if (($type | self::VALUE_SENSITIVE) === ($currType | self::VALUE_SENSITIVE)) {
945
+            return false;
946
+        }
947
+
948
+        // we complete with sensitive flag if the stored value is set as sensitive
949
+        if ($this->isTyped(self::VALUE_SENSITIVE, $currType)) {
950
+            $type = $type | self::VALUE_SENSITIVE;
951
+        }
952
+
953
+        $update = $this->connection->getQueryBuilder();
954
+        $update->update('appconfig')
955
+            ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
956
+            ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
957
+            ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
958
+        $update->executeStatement();
959
+        $this->valueTypes[$app][$key] = $type;
960
+
961
+        return true;
962
+    }
963
+
964
+
965
+    /**
966
+     * @inheritDoc
967
+     *
968
+     * @param string $app id of the app
969
+     * @param string $key config key
970
+     * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
971
+     *
972
+     * @return bool TRUE if entry was found in database and an update was necessary
973
+     * @since 29.0.0
974
+     */
975
+    public function updateSensitive(string $app, string $key, bool $sensitive): bool {
976
+        $this->assertParams($app, $key);
977
+        $this->loadConfig(lazy: true);
978
+        $this->matchAndApplyLexiconDefinition($app, $key);
979
+
980
+        try {
981
+            if ($sensitive === $this->isSensitive($app, $key, null)) {
982
+                return false;
983
+            }
984
+        } catch (AppConfigUnknownKeyException $e) {
985
+            return false;
986
+        }
987
+
988
+        $lazy = $this->isLazy($app, $key);
989
+        if ($lazy) {
990
+            $cache = $this->lazyCache;
991
+        } else {
992
+            $cache = $this->fastCache;
993
+        }
994
+
995
+        if (!isset($cache[$app][$key])) {
996
+            throw new AppConfigUnknownKeyException('unknown config key');
997
+        }
998
+
999
+        /**
1000
+         * type returned by getValueType() is already cleaned from sensitive flag
1001
+         * we just need to update it based on $sensitive and store it in database
1002
+         */
1003
+        $type = $this->getValueType($app, $key);
1004
+        $value = $cache[$app][$key];
1005
+        if ($sensitive) {
1006
+            $type |= self::VALUE_SENSITIVE;
1007
+            $value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value);
1008
+        } else {
1009
+            $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
1010
+        }
1011
+
1012
+        $update = $this->connection->getQueryBuilder();
1013
+        $update->update('appconfig')
1014
+            ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
1015
+            ->set('configvalue', $update->createNamedParameter($value))
1016
+            ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
1017
+            ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
1018
+        $update->executeStatement();
1019
+
1020
+        $this->valueTypes[$app][$key] = $type;
1021
+
1022
+        return true;
1023
+    }
1024
+
1025
+    /**
1026
+     * @inheritDoc
1027
+     *
1028
+     * @param string $app id of the app
1029
+     * @param string $key config key
1030
+     * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
1031
+     *
1032
+     * @return bool TRUE if entry was found in database and an update was necessary
1033
+     * @since 29.0.0
1034
+     */
1035
+    public function updateLazy(string $app, string $key, bool $lazy): bool {
1036
+        $this->assertParams($app, $key);
1037
+        $this->loadConfig(lazy: true);
1038
+        $this->matchAndApplyLexiconDefinition($app, $key);
1039
+
1040
+        try {
1041
+            if ($lazy === $this->isLazy($app, $key)) {
1042
+                return false;
1043
+            }
1044
+        } catch (AppConfigUnknownKeyException $e) {
1045
+            return false;
1046
+        }
1047
+
1048
+        $update = $this->connection->getQueryBuilder();
1049
+        $update->update('appconfig')
1050
+            ->set('lazy', $update->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT))
1051
+            ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
1052
+            ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
1053
+        $update->executeStatement();
1054
+
1055
+        // At this point, it is a lot safer to clean cache
1056
+        $this->clearCache();
1057
+
1058
+        return true;
1059
+    }
1060
+
1061
+    /**
1062
+     * @inheritDoc
1063
+     *
1064
+     * @param string $app id of the app
1065
+     * @param string $key config key
1066
+     *
1067
+     * @return array
1068
+     * @throws AppConfigUnknownKeyException if config key is not known in database
1069
+     * @since 29.0.0
1070
+     */
1071
+    public function getDetails(string $app, string $key): array {
1072
+        $this->assertParams($app, $key);
1073
+        $this->loadConfig(lazy: true);
1074
+        $this->matchAndApplyLexiconDefinition($app, $key);
1075
+        $lazy = $this->isLazy($app, $key);
1076
+
1077
+        if ($lazy) {
1078
+            $cache = $this->lazyCache;
1079
+        } else {
1080
+            $cache = $this->fastCache;
1081
+        }
1082
+
1083
+        $type = $this->getValueType($app, $key);
1084
+        try {
1085
+            $typeString = $this->convertTypeToString($type);
1086
+        } catch (AppConfigIncorrectTypeException $e) {
1087
+            $this->logger->warning('type stored in database is not correct', ['exception' => $e, 'type' => $type]);
1088
+            $typeString = (string)$type;
1089
+        }
1090
+
1091
+        if (!isset($cache[$app][$key])) {
1092
+            throw new AppConfigUnknownKeyException('unknown config key');
1093
+        }
1094
+
1095
+        $value = $cache[$app][$key];
1096
+        $sensitive = $this->isSensitive($app, $key, null);
1097
+        if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) {
1098
+            $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
1099
+        }
1100
+
1101
+        return [
1102
+            'app' => $app,
1103
+            'key' => $key,
1104
+            'value' => $value,
1105
+            'type' => $type,
1106
+            'lazy' => $lazy,
1107
+            'typeString' => $typeString,
1108
+            'sensitive' => $sensitive
1109
+        ];
1110
+    }
1111
+
1112
+    /**
1113
+     * @inheritDoc
1114
+     *
1115
+     * @param string $app id of the app
1116
+     * @param string $key config key
1117
+     *
1118
+     * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, internal?: bool, default?: string, definition?: string, note?: string}
1119
+     * @since 32.0.0
1120
+     */
1121
+    public function getKeyDetails(string $app, string $key): array {
1122
+        $this->assertParams($app, $key);
1123
+        try {
1124
+            $details = $this->getDetails($app, $key);
1125
+        } catch (AppConfigUnknownKeyException) {
1126
+            $details = [
1127
+                'app' => $app,
1128
+                'key' => $key
1129
+            ];
1130
+        }
1131
+
1132
+        /** @var Entry $lexiconEntry */
1133
+        try {
1134
+            $lazy = false;
1135
+            $this->matchAndApplyLexiconDefinition($app, $key, $lazy, lexiconEntry: $lexiconEntry);
1136
+        } catch (AppConfigTypeConflictException|AppConfigUnknownKeyException) {
1137
+            // can be ignored
1138
+        }
1139
+
1140
+        if ($lexiconEntry !== null) {
1141
+            $details = array_merge($details, [
1142
+                'lazy' => $lexiconEntry->isLazy(),
1143
+                'valueType' => $lexiconEntry->getValueType(),
1144
+                'valueTypeName' => $lexiconEntry->getValueType()->name,
1145
+                'sensitive' => $lexiconEntry->isFlagged(self::FLAG_SENSITIVE),
1146
+                'internal' => $lexiconEntry->isFlagged(self::FLAG_INTERNAL),
1147
+                'default' => $lexiconEntry->getDefault($this->presetManager->getLexiconPreset()),
1148
+                'definition' => $lexiconEntry->getDefinition(),
1149
+                'note' => $lexiconEntry->getNote(),
1150
+            ]);
1151
+        }
1152
+
1153
+        return array_filter($details, static fn ($v): bool => ($v !== null));
1154
+    }
1155
+
1156
+    /**
1157
+     * @param string $type
1158
+     *
1159
+     * @return int
1160
+     * @throws AppConfigIncorrectTypeException
1161
+     * @since 29.0.0
1162
+     */
1163
+    public function convertTypeToInt(string $type): int {
1164
+        return match (strtolower($type)) {
1165
+            'mixed' => IAppConfig::VALUE_MIXED,
1166
+            'string' => IAppConfig::VALUE_STRING,
1167
+            'integer' => IAppConfig::VALUE_INT,
1168
+            'float' => IAppConfig::VALUE_FLOAT,
1169
+            'boolean' => IAppConfig::VALUE_BOOL,
1170
+            'array' => IAppConfig::VALUE_ARRAY,
1171
+            default => throw new AppConfigIncorrectTypeException('Unknown type ' . $type)
1172
+        };
1173
+    }
1174
+
1175
+    /**
1176
+     * @param int $type
1177
+     *
1178
+     * @return string
1179
+     * @throws AppConfigIncorrectTypeException
1180
+     * @since 29.0.0
1181
+     */
1182
+    public function convertTypeToString(int $type): string {
1183
+        $type &= ~self::VALUE_SENSITIVE;
1184
+
1185
+        return match ($type) {
1186
+            IAppConfig::VALUE_MIXED => 'mixed',
1187
+            IAppConfig::VALUE_STRING => 'string',
1188
+            IAppConfig::VALUE_INT => 'integer',
1189
+            IAppConfig::VALUE_FLOAT => 'float',
1190
+            IAppConfig::VALUE_BOOL => 'boolean',
1191
+            IAppConfig::VALUE_ARRAY => 'array',
1192
+            default => throw new AppConfigIncorrectTypeException('Unknown numeric type ' . $type)
1193
+        };
1194
+    }
1195
+
1196
+    /**
1197
+     * @inheritDoc
1198
+     *
1199
+     * @param string $app id of the app
1200
+     * @param string $key config key
1201
+     *
1202
+     * @since 29.0.0
1203
+     */
1204
+    public function deleteKey(string $app, string $key): void {
1205
+        $this->assertParams($app, $key);
1206
+        $this->matchAndApplyLexiconDefinition($app, $key);
1207
+
1208
+        $qb = $this->connection->getQueryBuilder();
1209
+        $qb->delete('appconfig')
1210
+            ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
1211
+            ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key)));
1212
+        $qb->executeStatement();
1213
+
1214
+        unset($this->lazyCache[$app][$key]);
1215
+        unset($this->fastCache[$app][$key]);
1216
+        unset($this->valueTypes[$app][$key]);
1217
+        $this->clearLocalCache();
1218
+    }
1219
+
1220
+    /**
1221
+     * @inheritDoc
1222
+     *
1223
+     * @param string $app id of the app
1224
+     *
1225
+     * @since 29.0.0
1226
+     */
1227
+    public function deleteApp(string $app): void {
1228
+        $this->assertParams($app);
1229
+        $qb = $this->connection->getQueryBuilder();
1230
+        $qb->delete('appconfig')
1231
+            ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)));
1232
+        $qb->executeStatement();
1233
+
1234
+        $this->clearCache();
1235
+    }
1236
+
1237
+    /**
1238
+     * @inheritDoc
1239
+     *
1240
+     * @param bool $reload set to TRUE to refill cache instantly after clearing it
1241
+     *
1242
+     * @internal
1243
+     * @since 29.0.0
1244
+     */
1245
+    public function clearCache(bool $reload = false): void {
1246
+        $this->lazyLoaded = $this->fastLoaded = false;
1247
+        $this->lazyCache = $this->fastCache = $this->valueTypes = $this->configLexiconDetails = [];
1248
+        $this->localCache?->remove(self::LOCAL_CACHE_KEY);
1249
+
1250
+        if (!$reload) {
1251
+            return;
1252
+        }
1253
+
1254
+        $this->loadConfig(lazy: true);
1255
+    }
1256
+
1257
+
1258
+    /**
1259
+     * For debug purpose.
1260
+     * Returns the cached data.
1261
+     *
1262
+     * @return array
1263
+     * @since 29.0.0
1264
+     * @internal
1265
+     */
1266
+    public function statusCache(): array {
1267
+        return [
1268
+            'fastLoaded' => $this->fastLoaded,
1269
+            'fastCache' => $this->fastCache,
1270
+            'lazyLoaded' => $this->lazyLoaded,
1271
+            'lazyCache' => $this->lazyCache,
1272
+        ];
1273
+    }
1274
+
1275
+    /**
1276
+     * @param int $needle bitflag to search
1277
+     * @param int $type known value
1278
+     *
1279
+     * @return bool TRUE if bitflag $needle is set in $type
1280
+     */
1281
+    private function isTyped(int $needle, int $type): bool {
1282
+        return (($needle & $type) !== 0);
1283
+    }
1284
+
1285
+    /**
1286
+     * Confirm the string set for app and key fit the database description
1287
+     *
1288
+     * @param string $app assert $app fit in database
1289
+     * @param string $configKey assert config key fit in database
1290
+     * @param bool $allowEmptyApp $app can be empty string
1291
+     * @param int $valueType assert value type is only one type
1292
+     *
1293
+     * @throws InvalidArgumentException
1294
+     */
1295
+    private function assertParams(string $app = '', string $configKey = '', bool $allowEmptyApp = false, int $valueType = -1): void {
1296
+        if (!$allowEmptyApp && $app === '') {
1297
+            throw new InvalidArgumentException('app cannot be an empty string');
1298
+        }
1299
+        if (strlen($app) > self::APP_MAX_LENGTH) {
1300
+            throw new InvalidArgumentException(
1301
+                'Value (' . $app . ') for app is too long (' . self::APP_MAX_LENGTH . ')'
1302
+            );
1303
+        }
1304
+        if (strlen($configKey) > self::KEY_MAX_LENGTH) {
1305
+            throw new InvalidArgumentException('Value (' . $configKey . ') for key is too long (' . self::KEY_MAX_LENGTH . ')');
1306
+        }
1307
+        if ($valueType > -1) {
1308
+            $valueType &= ~self::VALUE_SENSITIVE;
1309
+            if (!in_array($valueType, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
1310
+                throw new InvalidArgumentException('Unknown value type');
1311
+            }
1312
+        }
1313
+    }
1314
+
1315
+    /**
1316
+     * Load normal config or config set as lazy loaded
1317
+     *
1318
+     * @param bool $lazy set to TRUE to also load config values set as lazy loaded
1319
+     */
1320
+    private function loadConfig(?string $app = null, bool $lazy = false): void {
1321
+        if ($this->isLoaded($lazy)) {
1322
+            return;
1323
+        }
1324
+
1325
+        // if lazy is null or true, we debug log
1326
+        if ($lazy === true && $app !== null) {
1327
+            $exception = new \RuntimeException('The loading of lazy AppConfig values have been triggered by app "' . $app . '"');
1328
+            $this->logger->debug($exception->getMessage(), ['exception' => $exception, 'app' => $app]);
1329
+        }
1330
+
1331
+        $loadLazyOnly = $lazy && $this->isLoaded();
1332
+
1333
+        /** @var array<mixed> */
1334
+        $cacheContent = $this->localCache?->get(self::LOCAL_CACHE_KEY) ?? [];
1335
+        $includesLazyValues = !empty($cacheContent) && !empty($cacheContent['lazyCache']);
1336
+        if (!empty($cacheContent) && (!$lazy || $includesLazyValues)) {
1337
+            $this->valueTypes = $cacheContent['valueTypes'];
1338
+            $this->fastCache = $cacheContent['fastCache'];
1339
+            $this->fastLoaded = !empty($this->fastCache);
1340
+            if ($includesLazyValues) {
1341
+                $this->lazyCache = $cacheContent['lazyCache'];
1342
+                $this->lazyLoaded = !empty($this->lazyCache);
1343
+            }
1344
+            return;
1345
+        }
1346
+
1347
+        // Otherwise no cache available and we need to fetch from database
1348
+        $qb = $this->connection->getQueryBuilder();
1349
+        $qb->from('appconfig')
1350
+            ->select('appid', 'configkey', 'configvalue', 'type');
1351
+
1352
+        if ($lazy === false) {
1353
+            $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
1354
+        } else {
1355
+            if ($loadLazyOnly) {
1356
+                $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)));
1357
+            }
1358
+            $qb->addSelect('lazy');
1359
+        }
1360
+
1361
+        $result = $qb->executeQuery();
1362
+        $rows = $result->fetchAll();
1363
+        foreach ($rows as $row) {
1364
+            // most of the time, 'lazy' is not in the select because its value is already known
1365
+            if ($lazy && ((int)$row['lazy']) === 1) {
1366
+                $this->lazyCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
1367
+            } else {
1368
+                $this->fastCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
1369
+            }
1370
+            $this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0);
1371
+        }
1372
+
1373
+        $result->closeCursor();
1374
+        $this->localCache?->set(
1375
+            self::LOCAL_CACHE_KEY,
1376
+            [
1377
+                'fastCache' => $this->fastCache,
1378
+                'lazyCache' => $this->lazyCache,
1379
+                'valueTypes' => $this->valueTypes,
1380
+            ],
1381
+            self::LOCAL_CACHE_TTL,
1382
+        );
1383
+
1384
+        $this->fastLoaded = true;
1385
+        $this->lazyLoaded = $lazy;
1386
+    }
1387
+
1388
+    /**
1389
+     * @param bool $lazy - If set to true then also check if lazy values are loaded
1390
+     */
1391
+    private function isLoaded(bool $lazy = false): bool {
1392
+        return $this->fastLoaded && (!$lazy || $this->lazyLoaded);
1393
+    }
1394
+
1395
+    /**
1396
+     * Gets the config value
1397
+     *
1398
+     * @param string $app app
1399
+     * @param string $key key
1400
+     * @param string $default - Default value if the key does not exist
1401
+     *
1402
+     * @return string the value or $default
1403
+     * @deprecated 29.0.0 use getValue*()
1404
+     *
1405
+     * This function gets a value from the appconfig table. If the key does
1406
+     * not exist the default value will be returned
1407
+     */
1408
+    public function getValue($app, $key, $default = '') {
1409
+        $this->loadConfig($app);
1410
+        $this->matchAndApplyLexiconDefinition($app, $key);
1411
+
1412
+        return $this->fastCache[$app][$key] ?? $default;
1413
+    }
1414
+
1415
+    /**
1416
+     * Sets a value. If the key did not exist before it will be created.
1417
+     *
1418
+     * @param string $app app
1419
+     * @param string $key key
1420
+     * @param string|float|int $value value
1421
+     *
1422
+     * @return bool True if the value was inserted or updated, false if the value was the same
1423
+     * @throws AppConfigTypeConflictException
1424
+     * @throws AppConfigUnknownKeyException
1425
+     * @deprecated 29.0.0
1426
+     */
1427
+    public function setValue($app, $key, $value) {
1428
+        /**
1429
+         * TODO: would it be overkill, or decently improve performance, to catch
1430
+         * call to this method with $key='enabled' and 'hide' config value related
1431
+         * to $app when the app is disabled (by modifying entry in database: lazy=lazy+2)
1432
+         * or enabled (lazy=lazy-2)
1433
+         *
1434
+         * this solution would remove the loading of config values from disabled app
1435
+         * unless calling the method.
1436
+         */
1437
+        return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED);
1438
+    }
1439
+
1440
+
1441
+    /**
1442
+     * get multiple values, either the app or key can be used as wildcard by setting it to false
1443
+     *
1444
+     * @param string|false $app
1445
+     * @param string|false $key
1446
+     *
1447
+     * @return array|false
1448
+     * @deprecated 29.0.0 use {@see getAllValues()}
1449
+     */
1450
+    public function getValues($app, $key) {
1451
+        if (($app !== false) === ($key !== false)) {
1452
+            return false;
1453
+        }
1454
+
1455
+        $key = ($key === false) ? '' : $key;
1456
+        if (!$app) {
1457
+            return $this->searchValues($key, false, self::VALUE_MIXED);
1458
+        } else {
1459
+            return $this->getAllValues($app, $key);
1460
+        }
1461
+    }
1462
+
1463
+    /**
1464
+     * get all values of the app or and filters out sensitive data
1465
+     *
1466
+     * @param string $app
1467
+     *
1468
+     * @return array
1469
+     * @deprecated 29.0.0 use {@see getAllValues()}
1470
+     */
1471
+    public function getFilteredValues($app) {
1472
+        return $this->getAllValues($app, filtered: true);
1473
+    }
1474
+
1475
+
1476
+    /**
1477
+     * **Warning:** avoid default NULL value for $lazy as this will
1478
+     * load all lazy values from the database
1479
+     *
1480
+     * @param string $app
1481
+     * @param array<string, string> $values ['key' => 'value']
1482
+     * @param bool|null $lazy
1483
+     *
1484
+     * @return array<string, string|int|float|bool|array>
1485
+     */
1486
+    private function formatAppValues(string $app, array $values, ?bool $lazy = null): array {
1487
+        foreach ($values as $key => $value) {
1488
+            try {
1489
+                $type = $this->getValueType($app, $key, $lazy);
1490
+            } catch (AppConfigUnknownKeyException) {
1491
+                continue;
1492
+            }
1493
+
1494
+            $values[$key] = $this->convertTypedValue($value, $type);
1495
+        }
1496
+
1497
+        return $values;
1498
+    }
1499
+
1500
+    /**
1501
+     * convert string value to the expected type
1502
+     *
1503
+     * @param string $value
1504
+     * @param int $type
1505
+     *
1506
+     * @return string|int|float|bool|array
1507
+     */
1508
+    private function convertTypedValue(string $value, int $type): string|int|float|bool|array {
1509
+        switch ($type) {
1510
+            case self::VALUE_INT:
1511
+                return (int)$value;
1512
+            case self::VALUE_FLOAT:
1513
+                return (float)$value;
1514
+            case self::VALUE_BOOL:
1515
+                return in_array(strtolower($value), ['1', 'true', 'yes', 'on']);
1516
+            case self::VALUE_ARRAY:
1517
+                try {
1518
+                    return json_decode($value, true, flags: JSON_THROW_ON_ERROR);
1519
+                } catch (JsonException $e) {
1520
+                    // ignoreable
1521
+                }
1522
+                break;
1523
+        }
1524
+        return $value;
1525
+    }
1526
+
1527
+    /**
1528
+     * @param string $app
1529
+     *
1530
+     * @return string[]
1531
+     * @deprecated 29.0.0 data sensitivity should be set when calling setValue*()
1532
+     */
1533
+    private function getSensitiveKeys(string $app): array {
1534
+        $sensitiveValues = [
1535
+            'circles' => [
1536
+                '/^key_pairs$/',
1537
+                '/^local_gskey$/',
1538
+            ],
1539
+            'call_summary_bot' => [
1540
+                '/^secret_(.*)$/',
1541
+            ],
1542
+            'external' => [
1543
+                '/^sites$/',
1544
+                '/^jwt_token_privkey_(.*)$/',
1545
+            ],
1546
+            'globalsiteselector' => [
1547
+                '/^gss\.jwt\.key$/',
1548
+            ],
1549
+            'gpgmailer' => [
1550
+                '/^GpgServerKey$/',
1551
+            ],
1552
+            'integration_discourse' => [
1553
+                '/^private_key$/',
1554
+                '/^public_key$/',
1555
+            ],
1556
+            'integration_dropbox' => [
1557
+                '/^client_id$/',
1558
+                '/^client_secret$/',
1559
+            ],
1560
+            'integration_github' => [
1561
+                '/^client_id$/',
1562
+                '/^client_secret$/',
1563
+            ],
1564
+            'integration_gitlab' => [
1565
+                '/^client_id$/',
1566
+                '/^client_secret$/',
1567
+                '/^oauth_instance_url$/',
1568
+            ],
1569
+            'integration_google' => [
1570
+                '/^client_id$/',
1571
+                '/^client_secret$/',
1572
+            ],
1573
+            'integration_jira' => [
1574
+                '/^client_id$/',
1575
+                '/^client_secret$/',
1576
+                '/^forced_instance_url$/',
1577
+            ],
1578
+            'integration_onedrive' => [
1579
+                '/^client_id$/',
1580
+                '/^client_secret$/',
1581
+            ],
1582
+            'integration_openproject' => [
1583
+                '/^client_id$/',
1584
+                '/^client_secret$/',
1585
+                '/^oauth_instance_url$/',
1586
+            ],
1587
+            'integration_reddit' => [
1588
+                '/^client_id$/',
1589
+                '/^client_secret$/',
1590
+            ],
1591
+            'integration_suitecrm' => [
1592
+                '/^client_id$/',
1593
+                '/^client_secret$/',
1594
+                '/^oauth_instance_url$/',
1595
+            ],
1596
+            'integration_twitter' => [
1597
+                '/^consumer_key$/',
1598
+                '/^consumer_secret$/',
1599
+                '/^followed_user$/',
1600
+            ],
1601
+            'integration_zammad' => [
1602
+                '/^client_id$/',
1603
+                '/^client_secret$/',
1604
+                '/^oauth_instance_url$/',
1605
+            ],
1606
+            'maps' => [
1607
+                '/^mapboxAPIKEY$/',
1608
+            ],
1609
+            'notify_push' => [
1610
+                '/^cookie$/',
1611
+            ],
1612
+            'onlyoffice' => [
1613
+                '/^jwt_secret$/',
1614
+            ],
1615
+            'passwords' => [
1616
+                '/^SSEv1ServerKey$/',
1617
+            ],
1618
+            'serverinfo' => [
1619
+                '/^token$/',
1620
+            ],
1621
+            'spreed' => [
1622
+                '/^bridge_bot_password$/',
1623
+                '/^hosted-signaling-server-(.*)$/',
1624
+                '/^recording_servers$/',
1625
+                '/^signaling_servers$/',
1626
+                '/^signaling_ticket_secret$/',
1627
+                '/^signaling_token_privkey_(.*)$/',
1628
+                '/^signaling_token_pubkey_(.*)$/',
1629
+                '/^sip_bridge_dialin_info$/',
1630
+                '/^sip_bridge_shared_secret$/',
1631
+                '/^stun_servers$/',
1632
+                '/^turn_servers$/',
1633
+                '/^turn_server_secret$/',
1634
+            ],
1635
+            'support' => [
1636
+                '/^last_response$/',
1637
+                '/^potential_subscription_key$/',
1638
+                '/^subscription_key$/',
1639
+            ],
1640
+            'theming' => [
1641
+                '/^imprintUrl$/',
1642
+                '/^privacyUrl$/',
1643
+                '/^slogan$/',
1644
+                '/^url$/',
1645
+            ],
1646
+            'twofactor_gateway' => [
1647
+                '/^.*token$/',
1648
+            ],
1649
+            'user_ldap' => [
1650
+                '/^(s..)?ldap_agent_password$/',
1651
+            ],
1652
+            'user_saml' => [
1653
+                '/^idp-x509cert$/',
1654
+            ],
1655
+            'whiteboard' => [
1656
+                '/^jwt_secret_key$/',
1657
+            ],
1658
+        ];
1659
+
1660
+        return $sensitiveValues[$app] ?? [];
1661
+    }
1662
+
1663
+    /**
1664
+     * Clear all the cached app config values
1665
+     * New cache will be generated next time a config value is retrieved
1666
+     *
1667
+     * @deprecated 29.0.0 use {@see clearCache()}
1668
+     */
1669
+    public function clearCachedConfig(): void {
1670
+        $this->clearCache();
1671
+    }
1672
+
1673
+    /**
1674
+     * Match and apply current use of config values with defined lexicon.
1675
+     * Set $lazy to NULL only if only interested into checking that $key is alias.
1676
+     *
1677
+     * @throws AppConfigUnknownKeyException
1678
+     * @throws AppConfigTypeConflictException
1679
+     * @return bool TRUE if everything is fine compared to lexicon or lexicon does not exist
1680
+     */
1681
+    private function matchAndApplyLexiconDefinition(
1682
+        string $app,
1683
+        string &$key,
1684
+        ?bool &$lazy = null,
1685
+        int &$type = self::VALUE_MIXED,
1686
+        ?string &$default = null,
1687
+        ?Entry &$lexiconEntry = null,
1688
+    ): bool {
1689
+        if (in_array($key,
1690
+            [
1691
+                'enabled',
1692
+                'installed_version',
1693
+                'types',
1694
+            ])) {
1695
+            return true; // we don't break stuff for this list of config keys.
1696
+        }
1697
+        $configDetails = $this->getConfigDetailsFromLexicon($app);
1698
+        if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
1699
+            // in case '$rename' is set in ConfigLexiconEntry, we use the new config key
1700
+            $key = $configDetails['aliases'][$key];
1701
+        }
1702
+
1703
+        if (!array_key_exists($key, $configDetails['entries'])) {
1704
+            return $this->applyLexiconStrictness($configDetails['strictness'], $app . '/' . $key);
1705
+        }
1706
+
1707
+        // if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon
1708
+        if ($lazy === null) {
1709
+            return true;
1710
+        }
1711
+
1712
+        /** @var Entry $lexiconEntry */
1713
+        $lexiconEntry = $configDetails['entries'][$key];
1714
+        $type &= ~self::VALUE_SENSITIVE;
1715
+
1716
+        $appConfigValueType = $lexiconEntry->getValueType()->toAppConfigFlag();
1717
+        if ($type === self::VALUE_MIXED) {
1718
+            $type = $appConfigValueType; // we overwrite if value was requested as mixed
1719
+        } elseif ($appConfigValueType !== $type) {
1720
+            throw new AppConfigTypeConflictException('The app config key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon');
1721
+        }
1722
+
1723
+        $lazy = $lexiconEntry->isLazy();
1724
+        // only look for default if needed, default from Lexicon got priority
1725
+        if ($default !== null) {
1726
+            $default = $lexiconEntry->getDefault($this->presetManager->getLexiconPreset()) ?? $default;
1727
+        }
1728
+
1729
+        if ($lexiconEntry->isFlagged(self::FLAG_SENSITIVE)) {
1730
+            $type |= self::VALUE_SENSITIVE;
1731
+        }
1732
+        if ($lexiconEntry->isDeprecated()) {
1733
+            $this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.');
1734
+        }
1735
+
1736
+        return true;
1737
+    }
1738
+
1739
+    /**
1740
+     * manage ConfigLexicon behavior based on strictness set in IConfigLexicon
1741
+     *
1742
+     * @param Strictness|null $strictness
1743
+     * @param string $line
1744
+     *
1745
+     * @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed
1746
+     * @throws AppConfigUnknownKeyException if strictness implies exception
1747
+     * @see \OCP\Config\Lexicon\ILexicon::getStrictness()
1748
+     */
1749
+    private function applyLexiconStrictness(?Strictness $strictness, string $configAppKey): bool {
1750
+        if ($strictness === null) {
1751
+            return true;
1752
+        }
1753
+
1754
+        $line = 'The app config key ' . $configAppKey . ' is not defined in the config lexicon';
1755
+        switch ($strictness) {
1756
+            case Strictness::IGNORE:
1757
+                return true;
1758
+            case Strictness::NOTICE:
1759
+                if (!in_array($configAppKey, $this->strictnessApplied, true)) {
1760
+                    $this->strictnessApplied[] = $configAppKey;
1761
+                    $this->logger->notice($line);
1762
+                }
1763
+                return true;
1764
+            case Strictness::WARNING:
1765
+                if (!in_array($configAppKey, $this->strictnessApplied, true)) {
1766
+                    $this->strictnessApplied[] = $configAppKey;
1767
+                    $this->logger->warning($line);
1768
+                }
1769
+                return false;
1770
+        }
1771
+
1772
+        throw new AppConfigUnknownKeyException($line);
1773
+    }
1774
+
1775
+    /**
1776
+     * extract details from registered $appId's config lexicon
1777
+     *
1778
+     * @param string $appId
1779
+     * @internal
1780
+     *
1781
+     * @return array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}
1782
+     */
1783
+    public function getConfigDetailsFromLexicon(string $appId): array {
1784
+        if (!array_key_exists($appId, $this->configLexiconDetails)) {
1785
+            $entries = $aliases = [];
1786
+            $bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
1787
+            $configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
1788
+            foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
1789
+                $entries[$configEntry->getKey()] = $configEntry;
1790
+                $newName = $configEntry->getRename();
1791
+                if ($newName !== null) {
1792
+                    $aliases[$newName] = $configEntry->getKey();
1793
+                }
1794
+            }
1795
+
1796
+            $this->configLexiconDetails[$appId] = [
1797
+                'entries' => $entries,
1798
+                'aliases' => $aliases,
1799
+                'strictness' => $configLexicon?->getStrictness() ?? Strictness::IGNORE
1800
+            ];
1801
+        }
1802
+
1803
+        return $this->configLexiconDetails[$appId];
1804
+    }
1805
+
1806
+    /**
1807
+     * get Lexicon Entry using appId and config key entry
1808
+     *
1809
+     * @return Entry|null NULL if entry does not exist in app's Lexicon
1810
+     * @internal
1811
+     */
1812
+    public function getLexiconEntry(string $appId, string $key): ?Entry {
1813
+        return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null;
1814
+    }
1815
+
1816
+    /**
1817
+     * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class
1818
+     *
1819
+     * @internal
1820
+     */
1821
+    public function ignoreLexiconAliases(bool $ignore): void {
1822
+        $this->ignoreLexiconAliases = $ignore;
1823
+    }
1824
+
1825
+    /**
1826
+     * Returns the installed versions of all apps
1827
+     *
1828
+     * @return array<string, string>
1829
+     */
1830
+    public function getAppInstalledVersions(bool $onlyEnabled = false): array {
1831
+        if ($this->appVersionsCache === null) {
1832
+            /** @var array<string, string> */
1833
+            $this->appVersionsCache = $this->searchValues('installed_version', false, IAppConfig::VALUE_STRING);
1834
+        }
1835
+        if ($onlyEnabled) {
1836
+            return array_filter(
1837
+                $this->appVersionsCache,
1838
+                fn (string $app): bool => $this->getValueString($app, 'enabled', 'no') !== 'no',
1839
+                ARRAY_FILTER_USE_KEY
1840
+            );
1841
+        }
1842
+        return $this->appVersionsCache;
1843
+    }
1844
+
1845
+    private function clearLocalCache(): void {
1846
+        $this->localCache?->remove(self::LOCAL_CACHE_KEY);
1847
+    }
1848 1848
 }
Please login to merge, or discard this patch.
lib/public/Config/IUserConfig.php 2 patches
Indentation   +713 added lines, -713 removed lines patch added patch discarded remove patch
@@ -30,717 +30,717 @@
 block discarded – undo
30 30
  */
31 31
 #[Consumable(since: '32.0.0')]
32 32
 interface IUserConfig {
33
-	/**
34
-	 * @since 32.0.0
35
-	 */
36
-	public const FLAG_SENSITIVE = 1;   // value is sensitive
37
-	/**
38
-	 * @since 32.0.0
39
-	 */
40
-	public const FLAG_INDEXED = 2;    // value should be indexed
41
-	/**
42
-	 * @since 33.0.0
43
-	 */
44
-	public const FLAG_INTERNAL = 4;   // value is considered internal and can be hidden from listing
45
-
46
-	/**
47
-	 * Get list of all userIds with config stored in database.
48
-	 * If $appId is specified, will only limit the search to this value
49
-	 *
50
-	 * **WARNING:** ignore any cache and get data directly from database.
51
-	 *
52
-	 * @param string $appId optional id of app
53
-	 *
54
-	 * @return list<string> list of userIds
55
-	 *
56
-	 * @since 32.0.0
57
-	 */
58
-	public function getUserIds(string $appId = ''): array;
59
-
60
-	/**
61
-	 * Get list of all apps that have at least one config
62
-	 * value related to $userId stored in database
63
-	 *
64
-	 * **WARNING:** ignore lazy filtering, all user config are loaded from database
65
-	 *
66
-	 * @param string $userId id of the user
67
-	 *
68
-	 * @return list<string> list of app ids
69
-	 *
70
-	 * @since 32.0.0
71
-	 */
72
-	public function getApps(string $userId): array;
73
-
74
-	/**
75
-	 * Returns all keys stored in database, related to user+app.
76
-	 * Please note that the values are not returned.
77
-	 *
78
-	 * **WARNING:** ignore lazy filtering, all user config are loaded from database
79
-	 *
80
-	 * @param string $userId id of the user
81
-	 * @param string $app id of the app
82
-	 *
83
-	 * @return list<string> list of stored config keys
84
-	 *
85
-	 * @since 32.0.0
86
-	 */
87
-	public function getKeys(string $userId, string $app): array;
88
-
89
-	/**
90
-	 * Check if a key exists in the list of stored config values.
91
-	 *
92
-	 * @param string $userId id of the user
93
-	 * @param string $app id of the app
94
-	 * @param string $key config key
95
-	 * @param bool $lazy search within lazy loaded config
96
-	 *
97
-	 * @return bool TRUE if key exists
98
-	 *
99
-	 * @since 32.0.0
100
-	 */
101
-	public function hasKey(string $userId, string $app, string $key, ?bool $lazy = false): bool;
102
-
103
-	/**
104
-	 * best way to see if a value is set as sensitive (not displayed in report)
105
-	 *
106
-	 * @param string $userId id of the user
107
-	 * @param string $app id of the app
108
-	 * @param string $key config key
109
-	 * @param bool|null $lazy search within lazy loaded config
110
-	 *
111
-	 * @return bool TRUE if value is sensitive
112
-	 * @throws UnknownKeyException if config key is not known
113
-	 *
114
-	 * @since 32.0.0
115
-	 */
116
-	public function isSensitive(string $userId, string $app, string $key, ?bool $lazy = false): bool;
117
-
118
-	/**
119
-	 * best way to see if a value is set as indexed (so it can be search)
120
-	 *
121
-	 * @see self::searchUsersByValueString()
122
-	 * @see self::searchUsersByValueInt()
123
-	 * @see self::searchUsersByValueBool()
124
-	 * @see self::searchUsersByValues()
125
-	 *
126
-	 * @param string $userId id of the user
127
-	 * @param string $app id of the app
128
-	 * @param string $key config key
129
-	 * @param bool|null $lazy search within lazy loaded config
130
-	 *
131
-	 * @return bool TRUE if value is sensitive
132
-	 * @throws UnknownKeyException if config key is not known
133
-	 *
134
-	 * @since 32.0.0
135
-	 */
136
-	public function isIndexed(string $userId, string $app, string $key, ?bool $lazy = false): bool;
137
-
138
-	/**
139
-	 * Returns if the config key stored in database is lazy loaded
140
-	 *
141
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
142
-	 *
143
-	 * @param string $userId id of the user
144
-	 * @param string $app id of the app
145
-	 * @param string $key config key
146
-	 *
147
-	 * @return bool TRUE if config is lazy loaded
148
-	 * @throws UnknownKeyException if config key is not known
149
-	 * @see IUserConfig for details about lazy loading
150
-	 *
151
-	 * @since 32.0.0
152
-	 */
153
-	public function isLazy(string $userId, string $app, string $key): bool;
154
-
155
-	/**
156
-	 * List all config values from an app with config key starting with $key.
157
-	 * Returns an array with config key as key, stored value as value.
158
-	 *
159
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
160
-	 *
161
-	 * @param string $userId id of the user
162
-	 * @param string $app id of the app
163
-	 * @param string $prefix config keys prefix to search, can be empty.
164
-	 * @param bool $filtered filter sensitive config values
165
-	 *
166
-	 * @return array<string, string|int|float|bool|array> [key => value]
167
-	 *
168
-	 * @since 32.0.0
169
-	 */
170
-	public function getValues(string $userId, string $app, string $prefix = '', bool $filtered = false): array;
171
-
172
-	/**
173
-	 * List all config values of a user.
174
-	 * Returns an array with config key as key, stored value as value.
175
-	 *
176
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
177
-	 *
178
-	 * @param string $userId id of the user
179
-	 * @param bool $filtered filter sensitive config values
180
-	 *
181
-	 * @return array<string, string|int|float|bool|array> [key => value]
182
-	 *
183
-	 * @since 32.0.0
184
-	 */
185
-	public function getAllValues(string $userId, bool $filtered = false): array;
186
-
187
-	/**
188
-	 * List all apps storing a specific config key and its stored value.
189
-	 * Returns an array with appId as key, stored value as value.
190
-	 *
191
-	 * @param string $userId id of the user
192
-	 * @param string $key config key
193
-	 * @param bool $lazy search within lazy loaded config
194
-	 * @param ValueType|null $typedAs enforce type for the returned values
195
-	 *
196
-	 * @return array<string, string|int|float|bool|array> [appId => value]
197
-	 *
198
-	 * @since 32.0.0
199
-	 */
200
-	public function getValuesByApps(string $userId, string $key, bool $lazy = false, ?ValueType $typedAs = null): array;
201
-
202
-	/**
203
-	 * List all users storing a specific config key and its stored value.
204
-	 * Returns an array with userId as key, stored value as value.
205
-	 *
206
-	 * **WARNING:** no caching, generate a fresh request
207
-	 *
208
-	 * @param string $app id of the app
209
-	 * @param string $key config key
210
-	 * @param ValueType|null $typedAs enforce type for the returned values
211
-	 * @param array|null $userIds limit the search to a list of user ids
212
-	 *
213
-	 * @return array<string, string|int|float|bool|array> [userId => value]
214
-	 *
215
-	 * @since 32.0.0
216
-	 */
217
-	public function getValuesByUsers(string $app, string $key, ?ValueType $typedAs = null, ?array $userIds = null): array;
218
-
219
-	/**
220
-	 * List all users storing a specific config key/value pair.
221
-	 * Returns a list of user ids.
222
-	 *
223
-	 * **WARNING:** no caching, generate a fresh request
224
-	 *
225
-	 * @param string $app id of the app
226
-	 * @param string $key config key
227
-	 * @param string $value config value
228
-	 * @param bool $caseInsensitive non-case-sensitive search, only works if $value is a string
229
-	 *
230
-	 * @return Generator<string>
231
-	 *
232
-	 * @since 32.0.0
233
-	 */
234
-	public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): Generator;
235
-
236
-	/**
237
-	 * List all users storing a specific config key/value pair.
238
-	 * Returns a list of user ids.
239
-	 *
240
-	 * **WARNING:** no caching, generate a fresh request
241
-	 *
242
-	 * @param string $app id of the app
243
-	 * @param string $key config key
244
-	 * @param int $value config value
245
-	 *
246
-	 * @return Generator<string>
247
-	 *
248
-	 * @since 32.0.0
249
-	 */
250
-	public function searchUsersByValueInt(string $app, string $key, int $value): Generator;
251
-
252
-	/**
253
-	 * List all users storing a specific config key/value pair.
254
-	 * Returns a list of user ids.
255
-	 *
256
-	 * **WARNING:** no caching, generate a fresh request
257
-	 *
258
-	 * @param string $app id of the app
259
-	 * @param string $key config key
260
-	 * @param array $values list of possible config values
261
-	 *
262
-	 * @return Generator<string>
263
-	 *
264
-	 * @since 32.0.0
265
-	 */
266
-	public function searchUsersByValues(string $app, string $key, array $values): Generator;
267
-
268
-	/**
269
-	 * List all users storing a specific config key/value pair.
270
-	 * Returns a list of user ids.
271
-	 *
272
-	 * **WARNING:** no caching, generate a fresh request
273
-	 *
274
-	 * @param string $app id of the app
275
-	 * @param string $key config key
276
-	 * @param bool $value config value
277
-	 *
278
-	 * @return Generator<string>
279
-	 *
280
-	 * @since 32.0.0
281
-	 */
282
-	public function searchUsersByValueBool(string $app, string $key, bool $value): Generator;
283
-
284
-	/**
285
-	 * Get user config assigned to a config key.
286
-	 * If config key is not found in database, default value is returned.
287
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
288
-	 *
289
-	 * @param string $userId id of the user
290
-	 * @param string $app id of the app
291
-	 * @param string $key config key
292
-	 * @param string $default default value
293
-	 * @param bool $lazy search within lazy loaded config
294
-	 *
295
-	 * @return string stored config value or $default if not set in database
296
-	 *
297
-	 * @since 32.0.0
298
-	 *
299
-	 * @see IUserConfig for explanation about lazy loading
300
-	 * @see getValueInt()
301
-	 * @see getValueFloat()
302
-	 * @see getValueBool()
303
-	 * @see getValueArray()
304
-	 */
305
-	public function getValueString(string $userId, string $app, string $key, string $default = '', bool $lazy = false): string;
306
-
307
-	/**
308
-	 * Get config value assigned to a config key.
309
-	 * If config key is not found in database, default value is returned.
310
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
311
-	 *
312
-	 * @param string $userId id of the user
313
-	 * @param string $app id of the app
314
-	 * @param string $key config key
315
-	 * @param int $default default value
316
-	 * @param bool $lazy search within lazy loaded config
317
-	 *
318
-	 * @return int stored config value or $default if not set in database
319
-	 *
320
-	 * @since 32.0.0
321
-	 *
322
-	 * @see IUserConfig for explanation about lazy loading
323
-	 * @see getValueString()
324
-	 * @see getValueFloat()
325
-	 * @see getValueBool()
326
-	 * @see getValueArray()
327
-	 */
328
-	public function getValueInt(string $userId, string $app, string $key, int $default = 0, bool $lazy = false): int;
329
-
330
-	/**
331
-	 * Get config value assigned to a config key.
332
-	 * If config key is not found in database, default value is returned.
333
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
334
-	 *
335
-	 * @param string $userId id of the user
336
-	 * @param string $app id of the app
337
-	 * @param string $key config key
338
-	 * @param float $default default value
339
-	 * @param bool $lazy search within lazy loaded config
340
-	 *
341
-	 * @return float stored config value or $default if not set in database
342
-	 *
343
-	 * @since 32.0.0
344
-	 *
345
-	 * @see IUserConfig for explanation about lazy loading
346
-	 * @see getValueString()
347
-	 * @see getValueInt()
348
-	 * @see getValueBool()
349
-	 * @see getValueArray()
350
-	 */
351
-	public function getValueFloat(string $userId, string $app, string $key, float $default = 0, bool $lazy = false): float;
352
-
353
-	/**
354
-	 * Get config value assigned to a config key.
355
-	 * If config key is not found in database, default value is returned.
356
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
357
-	 *
358
-	 * @param string $userId id of the user
359
-	 * @param string $app id of the app
360
-	 * @param string $key config key
361
-	 * @param bool $default default value
362
-	 * @param bool $lazy search within lazy loaded config
363
-	 *
364
-	 * @return bool stored config value or $default if not set in database
365
-	 *
366
-	 * @since 32.0.0
367
-	 *
368
-	 * @see IUserPrefences for explanation about lazy loading
369
-	 * @see getValueString()
370
-	 * @see getValueInt()
371
-	 * @see getValueFloat()
372
-	 * @see getValueArray()
373
-	 */
374
-	public function getValueBool(string $userId, string $app, string $key, bool $default = false, bool $lazy = false): bool;
375
-
376
-	/**
377
-	 * Get config value assigned to a config key.
378
-	 * If config key is not found in database, default value is returned.
379
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
380
-	 *
381
-	 * @param string $userId id of the user
382
-	 * @param string $app id of the app
383
-	 * @param string $key config key
384
-	 * @param array $default default value`
385
-	 * @param bool $lazy search within lazy loaded config
386
-	 *
387
-	 * @return array stored config value or $default if not set in database
388
-	 *
389
-	 * @since 32.0.0
390
-	 *
391
-	 * @see IUserConfig for explanation about lazy loading
392
-	 * @see getValueString()
393
-	 * @see getValueInt()
394
-	 * @see getValueFloat()
395
-	 * @see getValueBool()
396
-	 */
397
-	public function getValueArray(string $userId, string $app, string $key, array $default = [], bool $lazy = false): array;
398
-
399
-	/**
400
-	 * returns the type of config value
401
-	 *
402
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
403
-	 *              unless lazy is set to false
404
-	 *
405
-	 * @param string $userId id of the user
406
-	 * @param string $app id of the app
407
-	 * @param string $key config key
408
-	 * @param bool|null $lazy
409
-	 *
410
-	 * @return ValueType type of the value
411
-	 * @throws UnknownKeyException if config key is not known
412
-	 * @throws IncorrectTypeException if config value type is not known
413
-	 *
414
-	 * @since 32.0.0
415
-	 */
416
-	public function getValueType(string $userId, string $app, string $key, ?bool $lazy = null): ValueType;
417
-
418
-	/**
419
-	 * returns a bitflag related to config value
420
-	 *
421
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
422
-	 *              unless lazy is set to false
423
-	 *
424
-	 * @param string $userId id of the user
425
-	 * @param string $app id of the app
426
-	 * @param string $key config key
427
-	 * @param bool $lazy lazy loading
428
-	 *
429
-	 * @return int a bitflag in relation to the config value
430
-	 * @throws UnknownKeyException if config key is not known
431
-	 * @throws IncorrectTypeException if config value type is not known
432
-	 *
433
-	 * @since 32.0.0
434
-	 */
435
-	public function getValueFlags(string $userId, string $app, string $key, bool $lazy = false): int;
436
-
437
-	/**
438
-	 * Store a config key and its value in database
439
-	 *
440
-	 * If config key is already known with the exact same config value, the database is not updated.
441
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
442
-	 *
443
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
444
-	 *
445
-	 * @param string $userId id of the user
446
-	 * @param string $app id of the app
447
-	 * @param string $key config key
448
-	 * @param string $value config value
449
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
450
-	 * @param bool $lazy set config as lazy loaded
451
-	 *
452
-	 * @return bool TRUE if value was different, therefor updated in database
453
-	 *
454
-	 * @since 32.0.0
455
-	 *
456
-	 * @see IUserConfig for explanation about lazy loading
457
-	 * @see setValueInt()
458
-	 * @see setValueFloat()
459
-	 * @see setValueBool()
460
-	 * @see setValueArray()
461
-	 */
462
-	public function setValueString(string $userId, string $app, string $key, string $value, bool $lazy = false, int $flags = 0): bool;
463
-
464
-	/**
465
-	 * Store a config key and its value in database
466
-	 *
467
-	 * When handling huge value around and/or above 2,147,483,647, a debug log will be generated
468
-	 * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers.
469
-	 *
470
-	 * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()}
471
-	 *
472
-	 * If config key is already known with the exact same config value, the database is not updated.
473
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
474
-	 *
475
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
476
-	 *
477
-	 * @param string $userId id of the user
478
-	 * @param string $app id of the app
479
-	 * @param string $key config key
480
-	 * @param int $value config value
481
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
482
-	 * @param bool $lazy set config as lazy loaded
483
-	 *
484
-	 * @return bool TRUE if value was different, therefor updated in database
485
-	 *
486
-	 * @since 32.0.0
487
-	 *
488
-	 * @see IUserConfig for explanation about lazy loading
489
-	 * @see setValueString()
490
-	 * @see setValueFloat()
491
-	 * @see setValueBool()
492
-	 * @see setValueArray()
493
-	 */
494
-	public function setValueInt(string $userId, string $app, string $key, int $value, bool $lazy = false, int $flags = 0): bool;
495
-
496
-	/**
497
-	 * Store a config key and its value in database.
498
-	 *
499
-	 * If config key is already known with the exact same config value, the database is not updated.
500
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
501
-	 *
502
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
503
-	 *
504
-	 * @param string $userId id of the user
505
-	 * @param string $app id of the app
506
-	 * @param string $key config key
507
-	 * @param float $value config value
508
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
509
-	 * @param bool $lazy set config as lazy loaded
510
-	 *
511
-	 * @return bool TRUE if value was different, therefor updated in database
512
-	 *
513
-	 * @since 32.0.0
514
-	 *
515
-	 * @see IUserConfig for explanation about lazy loading
516
-	 * @see setValueString()
517
-	 * @see setValueInt()
518
-	 * @see setValueBool()
519
-	 * @see setValueArray()
520
-	 */
521
-	public function setValueFloat(string $userId, string $app, string $key, float $value, bool $lazy = false, int $flags = 0): bool;
522
-
523
-	/**
524
-	 * Store a config key and its value in database
525
-	 *
526
-	 * If config key is already known with the exact same config value, the database is not updated.
527
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
528
-	 *
529
-	 * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first
530
-	 *
531
-	 * @param string $userId id of the user
532
-	 * @param string $app id of the app
533
-	 * @param string $key config key
534
-	 * @param bool $value config value
535
-	 * @param bool $lazy set config as lazy loaded
536
-	 *
537
-	 * @return bool TRUE if value was different, therefor updated in database
538
-	 *
539
-	 * @since 32.0.0
540
-	 *
541
-	 * @see IUserConfig for explanation about lazy loading
542
-	 * @see setValueString()
543
-	 * @see setValueInt()
544
-	 * @see setValueFloat()
545
-	 * @see setValueArray()
546
-	 */
547
-	public function setValueBool(string $userId, string $app, string $key, bool $value, bool $lazy = false): bool;
548
-
549
-	/**
550
-	 * Store a config key and its value in database
551
-	 *
552
-	 * If config key is already known with the exact same config value, the database is not updated.
553
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
554
-	 *
555
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
556
-	 *
557
-	 * @param string $userId id of the user
558
-	 * @param string $app id of the app
559
-	 * @param string $key config key
560
-	 * @param array $value config value
561
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
562
-	 * @param bool $lazy set config as lazy loaded
563
-	 *
564
-	 * @return bool TRUE if value was different, therefor updated in database
565
-	 *
566
-	 * @since 32.0.0
567
-	 *
568
-	 * @see IUserConfig for explanation about lazy loading
569
-	 * @see setValueString()
570
-	 * @see setValueInt()
571
-	 * @see setValueFloat()
572
-	 * @see setValueBool()
573
-	 */
574
-	public function setValueArray(string $userId, string $app, string $key, array $value, bool $lazy = false, int $flags = 0): bool;
575
-
576
-	/**
577
-	 * switch sensitive status of a config value
578
-	 *
579
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
580
-	 *
581
-	 * @param string $userId id of the user
582
-	 * @param string $app id of the app
583
-	 * @param string $key config key
584
-	 * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
585
-	 *
586
-	 * @return bool TRUE if database update were necessary
587
-	 *
588
-	 * @since 32.0.0
589
-	 */
590
-	public function updateSensitive(string $userId, string $app, string $key, bool $sensitive): bool;
591
-
592
-	/**
593
-	 * switch sensitive loading status of a config key for all users
594
-	 *
595
-	 * **Warning:** heavy on resources, MUST only be used on occ command or migrations
596
-	 *
597
-	 * @param string $app id of the app
598
-	 * @param string $key config key
599
-	 * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
600
-	 *
601
-	 * @since 32.0.0
602
-	 */
603
-	public function updateGlobalSensitive(string $app, string $key, bool $sensitive): void;
604
-
605
-
606
-	/**
607
-	 * switch indexed status of a config value
608
-	 *
609
-	 *  **WARNING:** ignore lazy filtering, all config values are loaded from database
610
-	 *
611
-	 * @param string $userId id of the user
612
-	 * @param string $app id of the app
613
-	 * @param string $key config key
614
-	 * @param bool $indexed TRUE to set as indexed, FALSE to unset
615
-	 *
616
-	 * @return bool TRUE if database update were necessary
617
-	 *
618
-	 * @since 32.0.0
619
-	 */
620
-	public function updateIndexed(string $userId, string $app, string $key, bool $indexed): bool;
621
-
622
-	/**
623
-	 * switch sensitive loading status of a config key for all users
624
-	 *
625
-	 * **Warning:** heavy on resources, MUST only be used on occ command or migrations
626
-	 *
627
-	 * @param string $app id of the app
628
-	 * @param string $key config key
629
-	 * @param bool $indexed TRUE to set as indexed, FALSE to unset
630
-	 *
631
-	 * @since 32.0.0
632
-	 */
633
-	public function updateGlobalIndexed(string $app, string $key, bool $indexed): void;
634
-
635
-	/**
636
-	 * switch lazy loading status of a config value
637
-	 *
638
-	 * @param string $userId id of the user
639
-	 * @param string $app id of the app
640
-	 * @param string $key config key
641
-	 * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
642
-	 *
643
-	 * @return bool TRUE if database update was necessary
644
-	 *
645
-	 * @since 32.0.0
646
-	 */
647
-	public function updateLazy(string $userId, string $app, string $key, bool $lazy): bool;
648
-
649
-	/**
650
-	 * switch lazy loading status of a config key for all users
651
-	 *
652
-	 * **Warning:** heavy on resources, MUST only be used on occ command or migrations
653
-	 *
654
-	 * @param string $app id of the app
655
-	 * @param string $key config key
656
-	 * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
657
-	 *
658
-	 * @since 32.0.0
659
-	 */
660
-	public function updateGlobalLazy(string $app, string $key, bool $lazy): void;
661
-
662
-	/**
663
-	 * returns an array contains details about a config value
664
-	 *
665
-	 * ```
666
-	 * [
667
-	 *   "app" => "myapp",
668
-	 *   "key" => "mykey",
669
-	 *   "value" => "its_value",
670
-	 *   "lazy" => false,
671
-	 *   "type" => 4,
672
-	 *   "typeString" => "string",
673
-	 *   'sensitive' => true
674
-	 * ]
675
-	 * ```
676
-	 *
677
-	 * @param string $userId id of the user
678
-	 * @param string $app id of the app
679
-	 * @param string $key config key
680
-	 *
681
-	 * @return array
682
-	 * @throws UnknownKeyException if config key is not known in database
683
-	 *
684
-	 * @since 32.0.0
685
-	 */
686
-	public function getDetails(string $userId, string $app, string $key): array;
687
-
688
-	/**
689
-	 * Delete single config key from database.
690
-	 *
691
-	 * @param string $userId id of the user
692
-	 * @param string $app id of the app
693
-	 * @param string $key config key
694
-	 *
695
-	 * @since 32.0.0
696
-	 */
697
-	public function deleteUserConfig(string $userId, string $app, string $key): void;
698
-
699
-	/**
700
-	 * Delete config values from all users linked to a specific config keys
701
-	 *
702
-	 * @param string $app id of the app
703
-	 * @param string $key config key
704
-	 *
705
-	 * @since 32.0.0
706
-	 */
707
-	public function deleteKey(string $app, string $key): void;
708
-
709
-	/**
710
-	 * delete all config keys linked to an app
711
-	 *
712
-	 * @param string $app id of the app
713
-	 *
714
-	 * @since 32.0.0
715
-	 */
716
-	public function deleteApp(string $app): void;
717
-
718
-	/**
719
-	 * delete all config keys linked to a user
720
-	 *
721
-	 * @param string $userId id of the user
722
-	 *
723
-	 * @since 32.0.0
724
-	 */
725
-	public function deleteAllUserConfig(string $userId): void;
726
-
727
-	/**
728
-	 * Clear the cache for a single user
729
-	 *
730
-	 * The cache will be rebuilt only the next time a user config is requested.
731
-	 *
732
-	 * @param string $userId id of the user
733
-	 * @param bool $reload set to TRUE to refill cache instantly after clearing it
734
-	 *
735
-	 * @since 32.0.0
736
-	 */
737
-	public function clearCache(string $userId, bool $reload = false): void;
738
-
739
-	/**
740
-	 * Clear the cache for all users.
741
-	 * The cache will be rebuilt only the next time a user config is requested.
742
-	 *
743
-	 * @since 32.0.0
744
-	 */
745
-	public function clearCacheAll(): void;
33
+    /**
34
+     * @since 32.0.0
35
+     */
36
+    public const FLAG_SENSITIVE = 1;   // value is sensitive
37
+    /**
38
+     * @since 32.0.0
39
+     */
40
+    public const FLAG_INDEXED = 2;    // value should be indexed
41
+    /**
42
+     * @since 33.0.0
43
+     */
44
+    public const FLAG_INTERNAL = 4;   // value is considered internal and can be hidden from listing
45
+
46
+    /**
47
+     * Get list of all userIds with config stored in database.
48
+     * If $appId is specified, will only limit the search to this value
49
+     *
50
+     * **WARNING:** ignore any cache and get data directly from database.
51
+     *
52
+     * @param string $appId optional id of app
53
+     *
54
+     * @return list<string> list of userIds
55
+     *
56
+     * @since 32.0.0
57
+     */
58
+    public function getUserIds(string $appId = ''): array;
59
+
60
+    /**
61
+     * Get list of all apps that have at least one config
62
+     * value related to $userId stored in database
63
+     *
64
+     * **WARNING:** ignore lazy filtering, all user config are loaded from database
65
+     *
66
+     * @param string $userId id of the user
67
+     *
68
+     * @return list<string> list of app ids
69
+     *
70
+     * @since 32.0.0
71
+     */
72
+    public function getApps(string $userId): array;
73
+
74
+    /**
75
+     * Returns all keys stored in database, related to user+app.
76
+     * Please note that the values are not returned.
77
+     *
78
+     * **WARNING:** ignore lazy filtering, all user config are loaded from database
79
+     *
80
+     * @param string $userId id of the user
81
+     * @param string $app id of the app
82
+     *
83
+     * @return list<string> list of stored config keys
84
+     *
85
+     * @since 32.0.0
86
+     */
87
+    public function getKeys(string $userId, string $app): array;
88
+
89
+    /**
90
+     * Check if a key exists in the list of stored config values.
91
+     *
92
+     * @param string $userId id of the user
93
+     * @param string $app id of the app
94
+     * @param string $key config key
95
+     * @param bool $lazy search within lazy loaded config
96
+     *
97
+     * @return bool TRUE if key exists
98
+     *
99
+     * @since 32.0.0
100
+     */
101
+    public function hasKey(string $userId, string $app, string $key, ?bool $lazy = false): bool;
102
+
103
+    /**
104
+     * best way to see if a value is set as sensitive (not displayed in report)
105
+     *
106
+     * @param string $userId id of the user
107
+     * @param string $app id of the app
108
+     * @param string $key config key
109
+     * @param bool|null $lazy search within lazy loaded config
110
+     *
111
+     * @return bool TRUE if value is sensitive
112
+     * @throws UnknownKeyException if config key is not known
113
+     *
114
+     * @since 32.0.0
115
+     */
116
+    public function isSensitive(string $userId, string $app, string $key, ?bool $lazy = false): bool;
117
+
118
+    /**
119
+     * best way to see if a value is set as indexed (so it can be search)
120
+     *
121
+     * @see self::searchUsersByValueString()
122
+     * @see self::searchUsersByValueInt()
123
+     * @see self::searchUsersByValueBool()
124
+     * @see self::searchUsersByValues()
125
+     *
126
+     * @param string $userId id of the user
127
+     * @param string $app id of the app
128
+     * @param string $key config key
129
+     * @param bool|null $lazy search within lazy loaded config
130
+     *
131
+     * @return bool TRUE if value is sensitive
132
+     * @throws UnknownKeyException if config key is not known
133
+     *
134
+     * @since 32.0.0
135
+     */
136
+    public function isIndexed(string $userId, string $app, string $key, ?bool $lazy = false): bool;
137
+
138
+    /**
139
+     * Returns if the config key stored in database is lazy loaded
140
+     *
141
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
142
+     *
143
+     * @param string $userId id of the user
144
+     * @param string $app id of the app
145
+     * @param string $key config key
146
+     *
147
+     * @return bool TRUE if config is lazy loaded
148
+     * @throws UnknownKeyException if config key is not known
149
+     * @see IUserConfig for details about lazy loading
150
+     *
151
+     * @since 32.0.0
152
+     */
153
+    public function isLazy(string $userId, string $app, string $key): bool;
154
+
155
+    /**
156
+     * List all config values from an app with config key starting with $key.
157
+     * Returns an array with config key as key, stored value as value.
158
+     *
159
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
160
+     *
161
+     * @param string $userId id of the user
162
+     * @param string $app id of the app
163
+     * @param string $prefix config keys prefix to search, can be empty.
164
+     * @param bool $filtered filter sensitive config values
165
+     *
166
+     * @return array<string, string|int|float|bool|array> [key => value]
167
+     *
168
+     * @since 32.0.0
169
+     */
170
+    public function getValues(string $userId, string $app, string $prefix = '', bool $filtered = false): array;
171
+
172
+    /**
173
+     * List all config values of a user.
174
+     * Returns an array with config key as key, stored value as value.
175
+     *
176
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
177
+     *
178
+     * @param string $userId id of the user
179
+     * @param bool $filtered filter sensitive config values
180
+     *
181
+     * @return array<string, string|int|float|bool|array> [key => value]
182
+     *
183
+     * @since 32.0.0
184
+     */
185
+    public function getAllValues(string $userId, bool $filtered = false): array;
186
+
187
+    /**
188
+     * List all apps storing a specific config key and its stored value.
189
+     * Returns an array with appId as key, stored value as value.
190
+     *
191
+     * @param string $userId id of the user
192
+     * @param string $key config key
193
+     * @param bool $lazy search within lazy loaded config
194
+     * @param ValueType|null $typedAs enforce type for the returned values
195
+     *
196
+     * @return array<string, string|int|float|bool|array> [appId => value]
197
+     *
198
+     * @since 32.0.0
199
+     */
200
+    public function getValuesByApps(string $userId, string $key, bool $lazy = false, ?ValueType $typedAs = null): array;
201
+
202
+    /**
203
+     * List all users storing a specific config key and its stored value.
204
+     * Returns an array with userId as key, stored value as value.
205
+     *
206
+     * **WARNING:** no caching, generate a fresh request
207
+     *
208
+     * @param string $app id of the app
209
+     * @param string $key config key
210
+     * @param ValueType|null $typedAs enforce type for the returned values
211
+     * @param array|null $userIds limit the search to a list of user ids
212
+     *
213
+     * @return array<string, string|int|float|bool|array> [userId => value]
214
+     *
215
+     * @since 32.0.0
216
+     */
217
+    public function getValuesByUsers(string $app, string $key, ?ValueType $typedAs = null, ?array $userIds = null): array;
218
+
219
+    /**
220
+     * List all users storing a specific config key/value pair.
221
+     * Returns a list of user ids.
222
+     *
223
+     * **WARNING:** no caching, generate a fresh request
224
+     *
225
+     * @param string $app id of the app
226
+     * @param string $key config key
227
+     * @param string $value config value
228
+     * @param bool $caseInsensitive non-case-sensitive search, only works if $value is a string
229
+     *
230
+     * @return Generator<string>
231
+     *
232
+     * @since 32.0.0
233
+     */
234
+    public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): Generator;
235
+
236
+    /**
237
+     * List all users storing a specific config key/value pair.
238
+     * Returns a list of user ids.
239
+     *
240
+     * **WARNING:** no caching, generate a fresh request
241
+     *
242
+     * @param string $app id of the app
243
+     * @param string $key config key
244
+     * @param int $value config value
245
+     *
246
+     * @return Generator<string>
247
+     *
248
+     * @since 32.0.0
249
+     */
250
+    public function searchUsersByValueInt(string $app, string $key, int $value): Generator;
251
+
252
+    /**
253
+     * List all users storing a specific config key/value pair.
254
+     * Returns a list of user ids.
255
+     *
256
+     * **WARNING:** no caching, generate a fresh request
257
+     *
258
+     * @param string $app id of the app
259
+     * @param string $key config key
260
+     * @param array $values list of possible config values
261
+     *
262
+     * @return Generator<string>
263
+     *
264
+     * @since 32.0.0
265
+     */
266
+    public function searchUsersByValues(string $app, string $key, array $values): Generator;
267
+
268
+    /**
269
+     * List all users storing a specific config key/value pair.
270
+     * Returns a list of user ids.
271
+     *
272
+     * **WARNING:** no caching, generate a fresh request
273
+     *
274
+     * @param string $app id of the app
275
+     * @param string $key config key
276
+     * @param bool $value config value
277
+     *
278
+     * @return Generator<string>
279
+     *
280
+     * @since 32.0.0
281
+     */
282
+    public function searchUsersByValueBool(string $app, string $key, bool $value): Generator;
283
+
284
+    /**
285
+     * Get user config assigned to a config key.
286
+     * If config key is not found in database, default value is returned.
287
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
288
+     *
289
+     * @param string $userId id of the user
290
+     * @param string $app id of the app
291
+     * @param string $key config key
292
+     * @param string $default default value
293
+     * @param bool $lazy search within lazy loaded config
294
+     *
295
+     * @return string stored config value or $default if not set in database
296
+     *
297
+     * @since 32.0.0
298
+     *
299
+     * @see IUserConfig for explanation about lazy loading
300
+     * @see getValueInt()
301
+     * @see getValueFloat()
302
+     * @see getValueBool()
303
+     * @see getValueArray()
304
+     */
305
+    public function getValueString(string $userId, string $app, string $key, string $default = '', bool $lazy = false): string;
306
+
307
+    /**
308
+     * Get config value assigned to a config key.
309
+     * If config key is not found in database, default value is returned.
310
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
311
+     *
312
+     * @param string $userId id of the user
313
+     * @param string $app id of the app
314
+     * @param string $key config key
315
+     * @param int $default default value
316
+     * @param bool $lazy search within lazy loaded config
317
+     *
318
+     * @return int stored config value or $default if not set in database
319
+     *
320
+     * @since 32.0.0
321
+     *
322
+     * @see IUserConfig for explanation about lazy loading
323
+     * @see getValueString()
324
+     * @see getValueFloat()
325
+     * @see getValueBool()
326
+     * @see getValueArray()
327
+     */
328
+    public function getValueInt(string $userId, string $app, string $key, int $default = 0, bool $lazy = false): int;
329
+
330
+    /**
331
+     * Get config value assigned to a config key.
332
+     * If config key is not found in database, default value is returned.
333
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
334
+     *
335
+     * @param string $userId id of the user
336
+     * @param string $app id of the app
337
+     * @param string $key config key
338
+     * @param float $default default value
339
+     * @param bool $lazy search within lazy loaded config
340
+     *
341
+     * @return float stored config value or $default if not set in database
342
+     *
343
+     * @since 32.0.0
344
+     *
345
+     * @see IUserConfig for explanation about lazy loading
346
+     * @see getValueString()
347
+     * @see getValueInt()
348
+     * @see getValueBool()
349
+     * @see getValueArray()
350
+     */
351
+    public function getValueFloat(string $userId, string $app, string $key, float $default = 0, bool $lazy = false): float;
352
+
353
+    /**
354
+     * Get config value assigned to a config key.
355
+     * If config key is not found in database, default value is returned.
356
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
357
+     *
358
+     * @param string $userId id of the user
359
+     * @param string $app id of the app
360
+     * @param string $key config key
361
+     * @param bool $default default value
362
+     * @param bool $lazy search within lazy loaded config
363
+     *
364
+     * @return bool stored config value or $default if not set in database
365
+     *
366
+     * @since 32.0.0
367
+     *
368
+     * @see IUserPrefences for explanation about lazy loading
369
+     * @see getValueString()
370
+     * @see getValueInt()
371
+     * @see getValueFloat()
372
+     * @see getValueArray()
373
+     */
374
+    public function getValueBool(string $userId, string $app, string $key, bool $default = false, bool $lazy = false): bool;
375
+
376
+    /**
377
+     * Get config value assigned to a config key.
378
+     * If config key is not found in database, default value is returned.
379
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
380
+     *
381
+     * @param string $userId id of the user
382
+     * @param string $app id of the app
383
+     * @param string $key config key
384
+     * @param array $default default value`
385
+     * @param bool $lazy search within lazy loaded config
386
+     *
387
+     * @return array stored config value or $default if not set in database
388
+     *
389
+     * @since 32.0.0
390
+     *
391
+     * @see IUserConfig for explanation about lazy loading
392
+     * @see getValueString()
393
+     * @see getValueInt()
394
+     * @see getValueFloat()
395
+     * @see getValueBool()
396
+     */
397
+    public function getValueArray(string $userId, string $app, string $key, array $default = [], bool $lazy = false): array;
398
+
399
+    /**
400
+     * returns the type of config value
401
+     *
402
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
403
+     *              unless lazy is set to false
404
+     *
405
+     * @param string $userId id of the user
406
+     * @param string $app id of the app
407
+     * @param string $key config key
408
+     * @param bool|null $lazy
409
+     *
410
+     * @return ValueType type of the value
411
+     * @throws UnknownKeyException if config key is not known
412
+     * @throws IncorrectTypeException if config value type is not known
413
+     *
414
+     * @since 32.0.0
415
+     */
416
+    public function getValueType(string $userId, string $app, string $key, ?bool $lazy = null): ValueType;
417
+
418
+    /**
419
+     * returns a bitflag related to config value
420
+     *
421
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
422
+     *              unless lazy is set to false
423
+     *
424
+     * @param string $userId id of the user
425
+     * @param string $app id of the app
426
+     * @param string $key config key
427
+     * @param bool $lazy lazy loading
428
+     *
429
+     * @return int a bitflag in relation to the config value
430
+     * @throws UnknownKeyException if config key is not known
431
+     * @throws IncorrectTypeException if config value type is not known
432
+     *
433
+     * @since 32.0.0
434
+     */
435
+    public function getValueFlags(string $userId, string $app, string $key, bool $lazy = false): int;
436
+
437
+    /**
438
+     * Store a config key and its value in database
439
+     *
440
+     * If config key is already known with the exact same config value, the database is not updated.
441
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
442
+     *
443
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
444
+     *
445
+     * @param string $userId id of the user
446
+     * @param string $app id of the app
447
+     * @param string $key config key
448
+     * @param string $value config value
449
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
450
+     * @param bool $lazy set config as lazy loaded
451
+     *
452
+     * @return bool TRUE if value was different, therefor updated in database
453
+     *
454
+     * @since 32.0.0
455
+     *
456
+     * @see IUserConfig for explanation about lazy loading
457
+     * @see setValueInt()
458
+     * @see setValueFloat()
459
+     * @see setValueBool()
460
+     * @see setValueArray()
461
+     */
462
+    public function setValueString(string $userId, string $app, string $key, string $value, bool $lazy = false, int $flags = 0): bool;
463
+
464
+    /**
465
+     * Store a config key and its value in database
466
+     *
467
+     * When handling huge value around and/or above 2,147,483,647, a debug log will be generated
468
+     * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers.
469
+     *
470
+     * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()}
471
+     *
472
+     * If config key is already known with the exact same config value, the database is not updated.
473
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
474
+     *
475
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
476
+     *
477
+     * @param string $userId id of the user
478
+     * @param string $app id of the app
479
+     * @param string $key config key
480
+     * @param int $value config value
481
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
482
+     * @param bool $lazy set config as lazy loaded
483
+     *
484
+     * @return bool TRUE if value was different, therefor updated in database
485
+     *
486
+     * @since 32.0.0
487
+     *
488
+     * @see IUserConfig for explanation about lazy loading
489
+     * @see setValueString()
490
+     * @see setValueFloat()
491
+     * @see setValueBool()
492
+     * @see setValueArray()
493
+     */
494
+    public function setValueInt(string $userId, string $app, string $key, int $value, bool $lazy = false, int $flags = 0): bool;
495
+
496
+    /**
497
+     * Store a config key and its value in database.
498
+     *
499
+     * If config key is already known with the exact same config value, the database is not updated.
500
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
501
+     *
502
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
503
+     *
504
+     * @param string $userId id of the user
505
+     * @param string $app id of the app
506
+     * @param string $key config key
507
+     * @param float $value config value
508
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
509
+     * @param bool $lazy set config as lazy loaded
510
+     *
511
+     * @return bool TRUE if value was different, therefor updated in database
512
+     *
513
+     * @since 32.0.0
514
+     *
515
+     * @see IUserConfig for explanation about lazy loading
516
+     * @see setValueString()
517
+     * @see setValueInt()
518
+     * @see setValueBool()
519
+     * @see setValueArray()
520
+     */
521
+    public function setValueFloat(string $userId, string $app, string $key, float $value, bool $lazy = false, int $flags = 0): bool;
522
+
523
+    /**
524
+     * Store a config key and its value in database
525
+     *
526
+     * If config key is already known with the exact same config value, the database is not updated.
527
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
528
+     *
529
+     * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first
530
+     *
531
+     * @param string $userId id of the user
532
+     * @param string $app id of the app
533
+     * @param string $key config key
534
+     * @param bool $value config value
535
+     * @param bool $lazy set config as lazy loaded
536
+     *
537
+     * @return bool TRUE if value was different, therefor updated in database
538
+     *
539
+     * @since 32.0.0
540
+     *
541
+     * @see IUserConfig for explanation about lazy loading
542
+     * @see setValueString()
543
+     * @see setValueInt()
544
+     * @see setValueFloat()
545
+     * @see setValueArray()
546
+     */
547
+    public function setValueBool(string $userId, string $app, string $key, bool $value, bool $lazy = false): bool;
548
+
549
+    /**
550
+     * Store a config key and its value in database
551
+     *
552
+     * If config key is already known with the exact same config value, the database is not updated.
553
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
554
+     *
555
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
556
+     *
557
+     * @param string $userId id of the user
558
+     * @param string $app id of the app
559
+     * @param string $key config key
560
+     * @param array $value config value
561
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
562
+     * @param bool $lazy set config as lazy loaded
563
+     *
564
+     * @return bool TRUE if value was different, therefor updated in database
565
+     *
566
+     * @since 32.0.0
567
+     *
568
+     * @see IUserConfig for explanation about lazy loading
569
+     * @see setValueString()
570
+     * @see setValueInt()
571
+     * @see setValueFloat()
572
+     * @see setValueBool()
573
+     */
574
+    public function setValueArray(string $userId, string $app, string $key, array $value, bool $lazy = false, int $flags = 0): bool;
575
+
576
+    /**
577
+     * switch sensitive status of a config value
578
+     *
579
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
580
+     *
581
+     * @param string $userId id of the user
582
+     * @param string $app id of the app
583
+     * @param string $key config key
584
+     * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
585
+     *
586
+     * @return bool TRUE if database update were necessary
587
+     *
588
+     * @since 32.0.0
589
+     */
590
+    public function updateSensitive(string $userId, string $app, string $key, bool $sensitive): bool;
591
+
592
+    /**
593
+     * switch sensitive loading status of a config key for all users
594
+     *
595
+     * **Warning:** heavy on resources, MUST only be used on occ command or migrations
596
+     *
597
+     * @param string $app id of the app
598
+     * @param string $key config key
599
+     * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
600
+     *
601
+     * @since 32.0.0
602
+     */
603
+    public function updateGlobalSensitive(string $app, string $key, bool $sensitive): void;
604
+
605
+
606
+    /**
607
+     * switch indexed status of a config value
608
+     *
609
+     *  **WARNING:** ignore lazy filtering, all config values are loaded from database
610
+     *
611
+     * @param string $userId id of the user
612
+     * @param string $app id of the app
613
+     * @param string $key config key
614
+     * @param bool $indexed TRUE to set as indexed, FALSE to unset
615
+     *
616
+     * @return bool TRUE if database update were necessary
617
+     *
618
+     * @since 32.0.0
619
+     */
620
+    public function updateIndexed(string $userId, string $app, string $key, bool $indexed): bool;
621
+
622
+    /**
623
+     * switch sensitive loading status of a config key for all users
624
+     *
625
+     * **Warning:** heavy on resources, MUST only be used on occ command or migrations
626
+     *
627
+     * @param string $app id of the app
628
+     * @param string $key config key
629
+     * @param bool $indexed TRUE to set as indexed, FALSE to unset
630
+     *
631
+     * @since 32.0.0
632
+     */
633
+    public function updateGlobalIndexed(string $app, string $key, bool $indexed): void;
634
+
635
+    /**
636
+     * switch lazy loading status of a config value
637
+     *
638
+     * @param string $userId id of the user
639
+     * @param string $app id of the app
640
+     * @param string $key config key
641
+     * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
642
+     *
643
+     * @return bool TRUE if database update was necessary
644
+     *
645
+     * @since 32.0.0
646
+     */
647
+    public function updateLazy(string $userId, string $app, string $key, bool $lazy): bool;
648
+
649
+    /**
650
+     * switch lazy loading status of a config key for all users
651
+     *
652
+     * **Warning:** heavy on resources, MUST only be used on occ command or migrations
653
+     *
654
+     * @param string $app id of the app
655
+     * @param string $key config key
656
+     * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
657
+     *
658
+     * @since 32.0.0
659
+     */
660
+    public function updateGlobalLazy(string $app, string $key, bool $lazy): void;
661
+
662
+    /**
663
+     * returns an array contains details about a config value
664
+     *
665
+     * ```
666
+     * [
667
+     *   "app" => "myapp",
668
+     *   "key" => "mykey",
669
+     *   "value" => "its_value",
670
+     *   "lazy" => false,
671
+     *   "type" => 4,
672
+     *   "typeString" => "string",
673
+     *   'sensitive' => true
674
+     * ]
675
+     * ```
676
+     *
677
+     * @param string $userId id of the user
678
+     * @param string $app id of the app
679
+     * @param string $key config key
680
+     *
681
+     * @return array
682
+     * @throws UnknownKeyException if config key is not known in database
683
+     *
684
+     * @since 32.0.0
685
+     */
686
+    public function getDetails(string $userId, string $app, string $key): array;
687
+
688
+    /**
689
+     * Delete single config key from database.
690
+     *
691
+     * @param string $userId id of the user
692
+     * @param string $app id of the app
693
+     * @param string $key config key
694
+     *
695
+     * @since 32.0.0
696
+     */
697
+    public function deleteUserConfig(string $userId, string $app, string $key): void;
698
+
699
+    /**
700
+     * Delete config values from all users linked to a specific config keys
701
+     *
702
+     * @param string $app id of the app
703
+     * @param string $key config key
704
+     *
705
+     * @since 32.0.0
706
+     */
707
+    public function deleteKey(string $app, string $key): void;
708
+
709
+    /**
710
+     * delete all config keys linked to an app
711
+     *
712
+     * @param string $app id of the app
713
+     *
714
+     * @since 32.0.0
715
+     */
716
+    public function deleteApp(string $app): void;
717
+
718
+    /**
719
+     * delete all config keys linked to a user
720
+     *
721
+     * @param string $userId id of the user
722
+     *
723
+     * @since 32.0.0
724
+     */
725
+    public function deleteAllUserConfig(string $userId): void;
726
+
727
+    /**
728
+     * Clear the cache for a single user
729
+     *
730
+     * The cache will be rebuilt only the next time a user config is requested.
731
+     *
732
+     * @param string $userId id of the user
733
+     * @param bool $reload set to TRUE to refill cache instantly after clearing it
734
+     *
735
+     * @since 32.0.0
736
+     */
737
+    public function clearCache(string $userId, bool $reload = false): void;
738
+
739
+    /**
740
+     * Clear the cache for all users.
741
+     * The cache will be rebuilt only the next time a user config is requested.
742
+     *
743
+     * @since 32.0.0
744
+     */
745
+    public function clearCacheAll(): void;
746 746
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -33,15 +33,15 @@
 block discarded – undo
33 33
 	/**
34 34
 	 * @since 32.0.0
35 35
 	 */
36
-	public const FLAG_SENSITIVE = 1;   // value is sensitive
36
+	public const FLAG_SENSITIVE = 1; // value is sensitive
37 37
 	/**
38 38
 	 * @since 32.0.0
39 39
 	 */
40
-	public const FLAG_INDEXED = 2;    // value should be indexed
40
+	public const FLAG_INDEXED = 2; // value should be indexed
41 41
 	/**
42 42
 	 * @since 33.0.0
43 43
 	 */
44
-	public const FLAG_INTERNAL = 4;   // value is considered internal and can be hidden from listing
44
+	public const FLAG_INTERNAL = 4; // value is considered internal and can be hidden from listing
45 45
 
46 46
 	/**
47 47
 	 * Get list of all userIds with config stored in database.
Please login to merge, or discard this patch.
lib/public/IAppConfig.php 2 patches
Indentation   +530 added lines, -530 removed lines patch added patch discarded remove patch
@@ -31,534 +31,534 @@
 block discarded – undo
31 31
  * @since 29.0.0 - Supporting types and lazy loading
32 32
  */
33 33
 interface IAppConfig {
34
-	/** @since 29.0.0 */
35
-	public const VALUE_SENSITIVE = 1;
36
-	/** @since 29.0.0 */
37
-	public const VALUE_MIXED = 2;
38
-	/** @since 29.0.0 */
39
-	public const VALUE_STRING = 4;
40
-	/** @since 29.0.0 */
41
-	public const VALUE_INT = 8;
42
-	/** @since 29.0.0 */
43
-	public const VALUE_FLOAT = 16;
44
-	/** @since 29.0.0 */
45
-	public const VALUE_BOOL = 32;
46
-	/** @since 29.0.0 */
47
-	public const VALUE_ARRAY = 64;
48
-
49
-	/** @since 31.0.0 */
50
-	public const FLAG_SENSITIVE = 1;   // value is sensitive
51
-	/**
52
-	 * @since 33.0.0
53
-	 */
54
-	public const FLAG_INTERNAL = 4;   // value is considered internal and can be hidden from listing
55
-
56
-	/**
57
-	 * Get list of all apps that have at least one config value stored in database
58
-	 *
59
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
60
-	 *
61
-	 * @return list<string> list of app ids
62
-	 * @since 7.0.0
63
-	 */
64
-	public function getApps(): array;
65
-
66
-	/**
67
-	 * Returns all keys stored in database, related to an app.
68
-	 * Please note that the values are not returned.
69
-	 *
70
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
71
-	 *
72
-	 * @param string $app id of the app
73
-	 * @return list<string> list of stored config keys
74
-	 * @see searchKeys to avoid loading lazy config keys
75
-	 *
76
-	 * @since 29.0.0
77
-	 */
78
-	public function getKeys(string $app): array;
79
-
80
-	/**
81
-	 * Returns list of keys stored in database, related to an app.
82
-	 * Please note that the values are not returned.
83
-	 *
84
-	 * @param string $app id of the app
85
-	 * @param string $prefix returns only keys starting with this value
86
-	 * @param bool $lazy TRUE to search in lazy config keys
87
-	 *
88
-	 * @return list<string> list of stored config keys
89
-	 * @since 32.0.0
90
-	 */
91
-	public function searchKeys(string $app, string $prefix = '', bool $lazy = false): array;
92
-
93
-	/**
94
-	 * Check if a key exists in the list of stored config values.
95
-	 *
96
-	 * @param string $app id of the app
97
-	 * @param string $key config key
98
-	 * @param bool $lazy search within lazy loaded config
99
-	 *
100
-	 * @return bool TRUE if key exists
101
-	 * @since 29.0.0 Added the $lazy argument
102
-	 * @since 7.0.0
103
-	 */
104
-	public function hasKey(string $app, string $key, ?bool $lazy = false): bool;
105
-
106
-	/**
107
-	 * best way to see if a value is set as sensitive (not displayed in report)
108
-	 *
109
-	 * @param string $app id of the app
110
-	 * @param string $key config key
111
-	 * @param bool|null $lazy search within lazy loaded config
112
-	 *
113
-	 * @return bool TRUE if value is sensitive
114
-	 * @throws AppConfigUnknownKeyException if config key is not known
115
-	 * @since 29.0.0
116
-	 */
117
-	public function isSensitive(string $app, string $key, ?bool $lazy = false): bool;
118
-
119
-	/**
120
-	 * Returns if the config key stored in database is lazy loaded
121
-	 *
122
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
123
-	 *
124
-	 * @param string $app id of the app
125
-	 * @param string $key config key
126
-	 *
127
-	 * @return bool TRUE if config is lazy loaded
128
-	 * @throws AppConfigUnknownKeyException if config key is not known
129
-	 * @see IAppConfig for details about lazy loading
130
-	 * @since 29.0.0
131
-	 */
132
-	public function isLazy(string $app, string $key): bool;
133
-
134
-	/**
135
-	 * List all config values from an app with config key starting with $key.
136
-	 * Returns an array with config key as key, stored value as value.
137
-	 *
138
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
139
-	 *
140
-	 * @param string $app id of the app
141
-	 * @param string $prefix config keys prefix to search, can be empty.
142
-	 * @param bool $filtered filter sensitive config values
143
-	 *
144
-	 * @return array<string, string|int|float|bool|array> [configKey => configValue]
145
-	 * @since 29.0.0
146
-	 */
147
-	public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array;
148
-
149
-	/**
150
-	 * List all apps storing a specific config key and its stored value.
151
-	 * Returns an array with appId as key, stored value as value.
152
-	 *
153
-	 * @param string $key config key
154
-	 * @param bool $lazy search within lazy loaded config
155
-	 * @param int|null $typedAs enforce type for the returned values {@see self::VALUE_STRING} and others
156
-	 *
157
-	 * @return array<string, string|int|float|bool|array> [appId => configValue]
158
-	 * @since 29.0.0
159
-	 */
160
-	public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array;
161
-
162
-	/**
163
-	 * Get config value assigned to a config key.
164
-	 * If config key is not found in database, default value is returned.
165
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
166
-	 *
167
-	 * @param string $app id of the app
168
-	 * @param string $key config key
169
-	 * @param string $default default value
170
-	 * @param bool $lazy search within lazy loaded config
171
-	 *
172
-	 * @return string stored config value or $default if not set in database
173
-	 * @since 29.0.0
174
-	 * @see IAppConfig for explanation about lazy loading
175
-	 * @see getValueInt()
176
-	 * @see getValueFloat()
177
-	 * @see getValueBool()
178
-	 * @see getValueArray()
179
-	 */
180
-	public function getValueString(string $app, string $key, string $default = '', bool $lazy = false): string;
181
-
182
-	/**
183
-	 * Get config value assigned to a config key.
184
-	 * If config key is not found in database, default value is returned.
185
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
186
-	 *
187
-	 * @param string $app id of the app
188
-	 * @param string $key config key
189
-	 * @param int $default default value
190
-	 * @param bool $lazy search within lazy loaded config
191
-	 *
192
-	 * @return int stored config value or $default if not set in database
193
-	 * @since 29.0.0
194
-	 * @see IAppConfig for explanation about lazy loading
195
-	 * @see getValueString()
196
-	 * @see getValueFloat()
197
-	 * @see getValueBool()
198
-	 * @see getValueArray()
199
-	 */
200
-	public function getValueInt(string $app, string $key, int $default = 0, bool $lazy = false): int;
201
-
202
-	/**
203
-	 * Get config value assigned to a config key.
204
-	 * If config key is not found in database, default value is returned.
205
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
206
-	 *
207
-	 * @param string $app id of the app
208
-	 * @param string $key config key
209
-	 * @param float $default default value
210
-	 * @param bool $lazy search within lazy loaded config
211
-	 *
212
-	 * @return float stored config value or $default if not set in database
213
-	 * @since 29.0.0
214
-	 * @see IAppConfig for explanation about lazy loading
215
-	 * @see getValueString()
216
-	 * @see getValueInt()
217
-	 * @see getValueBool()
218
-	 * @see getValueArray()
219
-	 */
220
-	public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float;
221
-
222
-	/**
223
-	 * Get config value assigned to a config key.
224
-	 * If config key is not found in database, default value is returned.
225
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
226
-	 *
227
-	 * @param string $app id of the app
228
-	 * @param string $key config key
229
-	 * @param bool $default default value
230
-	 * @param bool $lazy search within lazy loaded config
231
-	 *
232
-	 * @return bool stored config value or $default if not set in database
233
-	 * @since 29.0.0
234
-	 * @see IAppConfig for explanation about lazy loading
235
-	 * @see getValueString()
236
-	 * @see getValueInt()
237
-	 * @see getValueFloat()
238
-	 * @see getValueArray()
239
-	 */
240
-	public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool;
241
-
242
-	/**
243
-	 * Get config value assigned to a config key.
244
-	 * If config key is not found in database, default value is returned.
245
-	 * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
246
-	 *
247
-	 * @param string $app id of the app
248
-	 * @param string $key config key
249
-	 * @param array $default default value
250
-	 * @param bool $lazy search within lazy loaded config
251
-	 *
252
-	 * @return array stored config value or $default if not set in database
253
-	 * @since 29.0.0
254
-	 * @see IAppConfig for explanation about lazy loading
255
-	 * @see getValueString()
256
-	 * @see getValueInt()
257
-	 * @see getValueFloat()
258
-	 * @see getValueBool()
259
-	 */
260
-	public function getValueArray(string $app, string $key, array $default = [], bool $lazy = false): array;
261
-
262
-	/**
263
-	 * returns the type of config value
264
-	 *
265
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
266
-	 *              unless lazy is set to false
267
-	 *
268
-	 * @param string $app id of the app
269
-	 * @param string $key config key
270
-	 * @param bool|null $lazy
271
-	 *
272
-	 * @return int
273
-	 * @throws AppConfigUnknownKeyException
274
-	 * @since 29.0.0
275
-	 * @see VALUE_STRING
276
-	 * @see VALUE_INT
277
-	 * @see VALUE_FLOAT
278
-	 * @see VALUE_BOOL
279
-	 * @see VALUE_ARRAY
280
-	 */
281
-	public function getValueType(string $app, string $key, ?bool $lazy = null): int;
282
-
283
-	/**
284
-	 * Store a config key and its value in database
285
-	 *
286
-	 * If config key is already known with the exact same config value, the database is not updated.
287
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
288
-	 *
289
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
290
-	 *
291
-	 * @param string $app id of the app
292
-	 * @param string $key config key
293
-	 * @param string $value config value
294
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
295
-	 * @param bool $lazy set config as lazy loaded
296
-	 *
297
-	 * @return bool TRUE if value was different, therefor updated in database
298
-	 * @since 29.0.0
299
-	 * @see IAppConfig for explanation about lazy loading
300
-	 * @see setValueInt()
301
-	 * @see setValueFloat()
302
-	 * @see setValueBool()
303
-	 * @see setValueArray()
304
-	 */
305
-	public function setValueString(string $app, string $key, string $value, bool $lazy = false, bool $sensitive = false): bool;
306
-
307
-	/**
308
-	 * Store a config key and its value in database
309
-	 *
310
-	 * When handling huge value around and/or above 2,147,483,647, a debug log will be generated
311
-	 * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers.
312
-	 *
313
-	 * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()}
314
-	 *
315
-	 * If config key is already known with the exact same config value, the database is not updated.
316
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
317
-	 *
318
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
319
-	 *
320
-	 * @param string $app id of the app
321
-	 * @param string $key config key
322
-	 * @param int $value config value
323
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
324
-	 * @param bool $lazy set config as lazy loaded
325
-	 *
326
-	 * @return bool TRUE if value was different, therefor updated in database
327
-	 * @since 29.0.0
328
-	 * @see IAppConfig for explanation about lazy loading
329
-	 * @see setValueString()
330
-	 * @see setValueFloat()
331
-	 * @see setValueBool()
332
-	 * @see setValueArray()
333
-	 */
334
-	public function setValueInt(string $app, string $key, int $value, bool $lazy = false, bool $sensitive = false): bool;
335
-
336
-	/**
337
-	 * Store a config key and its value in database.
338
-	 *
339
-	 * If config key is already known with the exact same config value, the database is not updated.
340
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
341
-	 *
342
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
343
-	 *
344
-	 * @param string $app id of the app
345
-	 * @param string $key config key
346
-	 * @param float $value config value
347
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
348
-	 * @param bool $lazy set config as lazy loaded
349
-	 *
350
-	 * @return bool TRUE if value was different, therefor updated in database
351
-	 * @since 29.0.0
352
-	 * @see IAppConfig for explanation about lazy loading
353
-	 * @see setValueString()
354
-	 * @see setValueInt()
355
-	 * @see setValueBool()
356
-	 * @see setValueArray()
357
-	 */
358
-	public function setValueFloat(string $app, string $key, float $value, bool $lazy = false, bool $sensitive = false): bool;
359
-
360
-	/**
361
-	 * Store a config key and its value in database
362
-	 *
363
-	 * If config key is already known with the exact same config value, the database is not updated.
364
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
365
-	 *
366
-	 * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first
367
-	 *
368
-	 * @param string $app id of the app
369
-	 * @param string $key config key
370
-	 * @param bool $value config value
371
-	 * @param bool $lazy set config as lazy loaded
372
-	 *
373
-	 * @return bool TRUE if value was different, therefor updated in database
374
-	 * @since 29.0.0
375
-	 * @see IAppConfig for explanation about lazy loading
376
-	 * @see setValueString()
377
-	 * @see setValueInt()
378
-	 * @see setValueFloat()
379
-	 * @see setValueArray()
380
-	 */
381
-	public function setValueBool(string $app, string $key, bool $value, bool $lazy = false): bool;
382
-
383
-	/**
384
-	 * Store a config key and its value in database
385
-	 *
386
-	 * If config key is already known with the exact same config value, the database is not updated.
387
-	 * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
388
-	 *
389
-	 * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
390
-	 *
391
-	 * @param string $app id of the app
392
-	 * @param string $key config key
393
-	 * @param array $value config value
394
-	 * @param bool $sensitive if TRUE value will be hidden when listing config values.
395
-	 * @param bool $lazy set config as lazy loaded
396
-	 *
397
-	 * @return bool TRUE if value was different, therefor updated in database
398
-	 * @since 29.0.0
399
-	 * @see IAppConfig for explanation about lazy loading
400
-	 * @see setValueString()
401
-	 * @see setValueInt()
402
-	 * @see setValueFloat()
403
-	 * @see setValueBool()
404
-	 */
405
-	public function setValueArray(string $app, string $key, array $value, bool $lazy = false, bool $sensitive = false): bool;
406
-
407
-	/**
408
-	 * switch sensitive status of a config value
409
-	 *
410
-	 * **WARNING:** ignore lazy filtering, all config values are loaded from database
411
-	 *
412
-	 * @param string $app id of the app
413
-	 * @param string $key config key
414
-	 * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
415
-	 *
416
-	 * @return bool TRUE if database update were necessary
417
-	 * @since 29.0.0
418
-	 */
419
-	public function updateSensitive(string $app, string $key, bool $sensitive): bool;
420
-
421
-	/**
422
-	 * switch lazy loading status of a config value
423
-	 *
424
-	 * @param string $app id of the app
425
-	 * @param string $key config key
426
-	 * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
427
-	 *
428
-	 * @return bool TRUE if database update was necessary
429
-	 * @since 29.0.0
430
-	 */
431
-	public function updateLazy(string $app, string $key, bool $lazy): bool;
432
-
433
-	/**
434
-	 * returns an array contains details about a config value
435
-	 *
436
-	 * ```
437
-	 * [
438
-	 *   "app" => "myapp",
439
-	 *   "key" => "mykey",
440
-	 *   "value" => "its_value",
441
-	 *   "lazy" => false,
442
-	 *   "type" => 4,
443
-	 *   "typeString" => "string",
444
-	 *   'sensitive' => true
445
-	 * ]
446
-	 * ```
447
-	 *
448
-	 * @param string $app id of the app
449
-	 * @param string $key config key
450
-	 *
451
-	 * @return array
452
-	 * @throws AppConfigUnknownKeyException if config key is not known in database
453
-	 * @since 29.0.0
454
-	 */
455
-	public function getDetails(string $app, string $key): array;
456
-
457
-	/**
458
-	 * returns an array containing details about a config key.
459
-	 * key/value pair are available only if it exists.
460
-	 *
461
-	 * ```
462
-	 * [
463
-	 *   "app" => "myapp",
464
-	 *   "key" => "mykey",
465
-	 *   "value" => "current_value",
466
-	 *   "default" => "default_if_available",
467
-	 *   "definition" => "this is what it does",
468
-	 *   "note" => "enabling this is not compatible with that",
469
-	 *   "lazy" => false,
470
-	 *   "type" => 4,
471
-	 *   "typeString" => "string",
472
-	 *   'sensitive' => false
473
-	 * ]
474
-	 * ```
475
-	 *
476
-	 * @param string $app id of the app
477
-	 * @param string $key config key
478
-	 *
479
-	 * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, internal?: bool, default?: string, definition?: string, note?: string}
480
-	 * @since 32.0.0
481
-	 */
482
-	public function getKeyDetails(string $app, string $key): array;
483
-
484
-	/**
485
-	 * Convert string like 'string', 'integer', 'float', 'bool' or 'array' to
486
-	 * to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT},
487
-	 * {@see VALUE_BOOL} and {@see VALUE_ARRAY}
488
-	 *
489
-	 * @param string $type
490
-	 *
491
-	 * @return int
492
-	 * @since 29.0.0
493
-	 */
494
-	public function convertTypeToInt(string $type): int;
495
-
496
-	/**
497
-	 * Convert bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT},
498
-	 * {@see VALUE_BOOL} and {@see VALUE_ARRAY} to human-readable string
499
-	 *
500
-	 * @param int $type
501
-	 *
502
-	 * @return string
503
-	 * @since 29.0.0
504
-	 */
505
-	public function convertTypeToString(int $type): string;
506
-
507
-	/**
508
-	 * Delete single config key from database.
509
-	 *
510
-	 * @param string $app id of the app
511
-	 * @param string $key config key
512
-	 * @since 29.0.0
513
-	 */
514
-	public function deleteKey(string $app, string $key): void;
515
-
516
-	/**
517
-	 * delete all config keys linked to an app
518
-	 *
519
-	 * @param string $app id of the app
520
-	 * @since 29.0.0
521
-	 */
522
-	public function deleteApp(string $app): void;
523
-
524
-	/**
525
-	 * Clear the cache.
526
-	 *
527
-	 * The cache will be rebuilt only the next time a config value is requested.
528
-	 *
529
-	 * @param bool $reload set to TRUE to refill cache instantly after clearing it
530
-	 * @since 29.0.0
531
-	 */
532
-	public function clearCache(bool $reload = false): void;
533
-
534
-	/**
535
-	 * get multiply values, either the app or key can be used as wildcard by setting it to false
536
-	 *
537
-	 * @param string|false $key
538
-	 * @param string|false $app
539
-	 *
540
-	 * @return array|false
541
-	 * @since 7.0.0
542
-	 * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()}
543
-	 */
544
-	public function getValues($app, $key);
545
-
546
-	/**
547
-	 * get all values of the app or and filters out sensitive data
548
-	 *
549
-	 * @param string $app
550
-	 *
551
-	 * @return array
552
-	 * @since 12.0.0
553
-	 * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()}
554
-	 */
555
-	public function getFilteredValues($app);
556
-
557
-	/**
558
-	 * Returns the installed version of all apps
559
-	 *
560
-	 * @return array<string, string>
561
-	 * @since 32.0.0
562
-	 */
563
-	public function getAppInstalledVersions(bool $onlyEnabled = false): array;
34
+    /** @since 29.0.0 */
35
+    public const VALUE_SENSITIVE = 1;
36
+    /** @since 29.0.0 */
37
+    public const VALUE_MIXED = 2;
38
+    /** @since 29.0.0 */
39
+    public const VALUE_STRING = 4;
40
+    /** @since 29.0.0 */
41
+    public const VALUE_INT = 8;
42
+    /** @since 29.0.0 */
43
+    public const VALUE_FLOAT = 16;
44
+    /** @since 29.0.0 */
45
+    public const VALUE_BOOL = 32;
46
+    /** @since 29.0.0 */
47
+    public const VALUE_ARRAY = 64;
48
+
49
+    /** @since 31.0.0 */
50
+    public const FLAG_SENSITIVE = 1;   // value is sensitive
51
+    /**
52
+     * @since 33.0.0
53
+     */
54
+    public const FLAG_INTERNAL = 4;   // value is considered internal and can be hidden from listing
55
+
56
+    /**
57
+     * Get list of all apps that have at least one config value stored in database
58
+     *
59
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
60
+     *
61
+     * @return list<string> list of app ids
62
+     * @since 7.0.0
63
+     */
64
+    public function getApps(): array;
65
+
66
+    /**
67
+     * Returns all keys stored in database, related to an app.
68
+     * Please note that the values are not returned.
69
+     *
70
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
71
+     *
72
+     * @param string $app id of the app
73
+     * @return list<string> list of stored config keys
74
+     * @see searchKeys to avoid loading lazy config keys
75
+     *
76
+     * @since 29.0.0
77
+     */
78
+    public function getKeys(string $app): array;
79
+
80
+    /**
81
+     * Returns list of keys stored in database, related to an app.
82
+     * Please note that the values are not returned.
83
+     *
84
+     * @param string $app id of the app
85
+     * @param string $prefix returns only keys starting with this value
86
+     * @param bool $lazy TRUE to search in lazy config keys
87
+     *
88
+     * @return list<string> list of stored config keys
89
+     * @since 32.0.0
90
+     */
91
+    public function searchKeys(string $app, string $prefix = '', bool $lazy = false): array;
92
+
93
+    /**
94
+     * Check if a key exists in the list of stored config values.
95
+     *
96
+     * @param string $app id of the app
97
+     * @param string $key config key
98
+     * @param bool $lazy search within lazy loaded config
99
+     *
100
+     * @return bool TRUE if key exists
101
+     * @since 29.0.0 Added the $lazy argument
102
+     * @since 7.0.0
103
+     */
104
+    public function hasKey(string $app, string $key, ?bool $lazy = false): bool;
105
+
106
+    /**
107
+     * best way to see if a value is set as sensitive (not displayed in report)
108
+     *
109
+     * @param string $app id of the app
110
+     * @param string $key config key
111
+     * @param bool|null $lazy search within lazy loaded config
112
+     *
113
+     * @return bool TRUE if value is sensitive
114
+     * @throws AppConfigUnknownKeyException if config key is not known
115
+     * @since 29.0.0
116
+     */
117
+    public function isSensitive(string $app, string $key, ?bool $lazy = false): bool;
118
+
119
+    /**
120
+     * Returns if the config key stored in database is lazy loaded
121
+     *
122
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
123
+     *
124
+     * @param string $app id of the app
125
+     * @param string $key config key
126
+     *
127
+     * @return bool TRUE if config is lazy loaded
128
+     * @throws AppConfigUnknownKeyException if config key is not known
129
+     * @see IAppConfig for details about lazy loading
130
+     * @since 29.0.0
131
+     */
132
+    public function isLazy(string $app, string $key): bool;
133
+
134
+    /**
135
+     * List all config values from an app with config key starting with $key.
136
+     * Returns an array with config key as key, stored value as value.
137
+     *
138
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
139
+     *
140
+     * @param string $app id of the app
141
+     * @param string $prefix config keys prefix to search, can be empty.
142
+     * @param bool $filtered filter sensitive config values
143
+     *
144
+     * @return array<string, string|int|float|bool|array> [configKey => configValue]
145
+     * @since 29.0.0
146
+     */
147
+    public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array;
148
+
149
+    /**
150
+     * List all apps storing a specific config key and its stored value.
151
+     * Returns an array with appId as key, stored value as value.
152
+     *
153
+     * @param string $key config key
154
+     * @param bool $lazy search within lazy loaded config
155
+     * @param int|null $typedAs enforce type for the returned values {@see self::VALUE_STRING} and others
156
+     *
157
+     * @return array<string, string|int|float|bool|array> [appId => configValue]
158
+     * @since 29.0.0
159
+     */
160
+    public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array;
161
+
162
+    /**
163
+     * Get config value assigned to a config key.
164
+     * If config key is not found in database, default value is returned.
165
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
166
+     *
167
+     * @param string $app id of the app
168
+     * @param string $key config key
169
+     * @param string $default default value
170
+     * @param bool $lazy search within lazy loaded config
171
+     *
172
+     * @return string stored config value or $default if not set in database
173
+     * @since 29.0.0
174
+     * @see IAppConfig for explanation about lazy loading
175
+     * @see getValueInt()
176
+     * @see getValueFloat()
177
+     * @see getValueBool()
178
+     * @see getValueArray()
179
+     */
180
+    public function getValueString(string $app, string $key, string $default = '', bool $lazy = false): string;
181
+
182
+    /**
183
+     * Get config value assigned to a config key.
184
+     * If config key is not found in database, default value is returned.
185
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
186
+     *
187
+     * @param string $app id of the app
188
+     * @param string $key config key
189
+     * @param int $default default value
190
+     * @param bool $lazy search within lazy loaded config
191
+     *
192
+     * @return int stored config value or $default if not set in database
193
+     * @since 29.0.0
194
+     * @see IAppConfig for explanation about lazy loading
195
+     * @see getValueString()
196
+     * @see getValueFloat()
197
+     * @see getValueBool()
198
+     * @see getValueArray()
199
+     */
200
+    public function getValueInt(string $app, string $key, int $default = 0, bool $lazy = false): int;
201
+
202
+    /**
203
+     * Get config value assigned to a config key.
204
+     * If config key is not found in database, default value is returned.
205
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
206
+     *
207
+     * @param string $app id of the app
208
+     * @param string $key config key
209
+     * @param float $default default value
210
+     * @param bool $lazy search within lazy loaded config
211
+     *
212
+     * @return float stored config value or $default if not set in database
213
+     * @since 29.0.0
214
+     * @see IAppConfig for explanation about lazy loading
215
+     * @see getValueString()
216
+     * @see getValueInt()
217
+     * @see getValueBool()
218
+     * @see getValueArray()
219
+     */
220
+    public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float;
221
+
222
+    /**
223
+     * Get config value assigned to a config key.
224
+     * If config key is not found in database, default value is returned.
225
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
226
+     *
227
+     * @param string $app id of the app
228
+     * @param string $key config key
229
+     * @param bool $default default value
230
+     * @param bool $lazy search within lazy loaded config
231
+     *
232
+     * @return bool stored config value or $default if not set in database
233
+     * @since 29.0.0
234
+     * @see IAppConfig for explanation about lazy loading
235
+     * @see getValueString()
236
+     * @see getValueInt()
237
+     * @see getValueFloat()
238
+     * @see getValueArray()
239
+     */
240
+    public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool;
241
+
242
+    /**
243
+     * Get config value assigned to a config key.
244
+     * If config key is not found in database, default value is returned.
245
+     * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE.
246
+     *
247
+     * @param string $app id of the app
248
+     * @param string $key config key
249
+     * @param array $default default value
250
+     * @param bool $lazy search within lazy loaded config
251
+     *
252
+     * @return array stored config value or $default if not set in database
253
+     * @since 29.0.0
254
+     * @see IAppConfig for explanation about lazy loading
255
+     * @see getValueString()
256
+     * @see getValueInt()
257
+     * @see getValueFloat()
258
+     * @see getValueBool()
259
+     */
260
+    public function getValueArray(string $app, string $key, array $default = [], bool $lazy = false): array;
261
+
262
+    /**
263
+     * returns the type of config value
264
+     *
265
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
266
+     *              unless lazy is set to false
267
+     *
268
+     * @param string $app id of the app
269
+     * @param string $key config key
270
+     * @param bool|null $lazy
271
+     *
272
+     * @return int
273
+     * @throws AppConfigUnknownKeyException
274
+     * @since 29.0.0
275
+     * @see VALUE_STRING
276
+     * @see VALUE_INT
277
+     * @see VALUE_FLOAT
278
+     * @see VALUE_BOOL
279
+     * @see VALUE_ARRAY
280
+     */
281
+    public function getValueType(string $app, string $key, ?bool $lazy = null): int;
282
+
283
+    /**
284
+     * Store a config key and its value in database
285
+     *
286
+     * If config key is already known with the exact same config value, the database is not updated.
287
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
288
+     *
289
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
290
+     *
291
+     * @param string $app id of the app
292
+     * @param string $key config key
293
+     * @param string $value config value
294
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
295
+     * @param bool $lazy set config as lazy loaded
296
+     *
297
+     * @return bool TRUE if value was different, therefor updated in database
298
+     * @since 29.0.0
299
+     * @see IAppConfig for explanation about lazy loading
300
+     * @see setValueInt()
301
+     * @see setValueFloat()
302
+     * @see setValueBool()
303
+     * @see setValueArray()
304
+     */
305
+    public function setValueString(string $app, string $key, string $value, bool $lazy = false, bool $sensitive = false): bool;
306
+
307
+    /**
308
+     * Store a config key and its value in database
309
+     *
310
+     * When handling huge value around and/or above 2,147,483,647, a debug log will be generated
311
+     * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers.
312
+     *
313
+     * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()}
314
+     *
315
+     * If config key is already known with the exact same config value, the database is not updated.
316
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
317
+     *
318
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
319
+     *
320
+     * @param string $app id of the app
321
+     * @param string $key config key
322
+     * @param int $value config value
323
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
324
+     * @param bool $lazy set config as lazy loaded
325
+     *
326
+     * @return bool TRUE if value was different, therefor updated in database
327
+     * @since 29.0.0
328
+     * @see IAppConfig for explanation about lazy loading
329
+     * @see setValueString()
330
+     * @see setValueFloat()
331
+     * @see setValueBool()
332
+     * @see setValueArray()
333
+     */
334
+    public function setValueInt(string $app, string $key, int $value, bool $lazy = false, bool $sensitive = false): bool;
335
+
336
+    /**
337
+     * Store a config key and its value in database.
338
+     *
339
+     * If config key is already known with the exact same config value, the database is not updated.
340
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
341
+     *
342
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
343
+     *
344
+     * @param string $app id of the app
345
+     * @param string $key config key
346
+     * @param float $value config value
347
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
348
+     * @param bool $lazy set config as lazy loaded
349
+     *
350
+     * @return bool TRUE if value was different, therefor updated in database
351
+     * @since 29.0.0
352
+     * @see IAppConfig for explanation about lazy loading
353
+     * @see setValueString()
354
+     * @see setValueInt()
355
+     * @see setValueBool()
356
+     * @see setValueArray()
357
+     */
358
+    public function setValueFloat(string $app, string $key, float $value, bool $lazy = false, bool $sensitive = false): bool;
359
+
360
+    /**
361
+     * Store a config key and its value in database
362
+     *
363
+     * If config key is already known with the exact same config value, the database is not updated.
364
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
365
+     *
366
+     * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first
367
+     *
368
+     * @param string $app id of the app
369
+     * @param string $key config key
370
+     * @param bool $value config value
371
+     * @param bool $lazy set config as lazy loaded
372
+     *
373
+     * @return bool TRUE if value was different, therefor updated in database
374
+     * @since 29.0.0
375
+     * @see IAppConfig for explanation about lazy loading
376
+     * @see setValueString()
377
+     * @see setValueInt()
378
+     * @see setValueFloat()
379
+     * @see setValueArray()
380
+     */
381
+    public function setValueBool(string $app, string $key, bool $value, bool $lazy = false): bool;
382
+
383
+    /**
384
+     * Store a config key and its value in database
385
+     *
386
+     * If config key is already known with the exact same config value, the database is not updated.
387
+     * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded.
388
+     *
389
+     * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first
390
+     *
391
+     * @param string $app id of the app
392
+     * @param string $key config key
393
+     * @param array $value config value
394
+     * @param bool $sensitive if TRUE value will be hidden when listing config values.
395
+     * @param bool $lazy set config as lazy loaded
396
+     *
397
+     * @return bool TRUE if value was different, therefor updated in database
398
+     * @since 29.0.0
399
+     * @see IAppConfig for explanation about lazy loading
400
+     * @see setValueString()
401
+     * @see setValueInt()
402
+     * @see setValueFloat()
403
+     * @see setValueBool()
404
+     */
405
+    public function setValueArray(string $app, string $key, array $value, bool $lazy = false, bool $sensitive = false): bool;
406
+
407
+    /**
408
+     * switch sensitive status of a config value
409
+     *
410
+     * **WARNING:** ignore lazy filtering, all config values are loaded from database
411
+     *
412
+     * @param string $app id of the app
413
+     * @param string $key config key
414
+     * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
415
+     *
416
+     * @return bool TRUE if database update were necessary
417
+     * @since 29.0.0
418
+     */
419
+    public function updateSensitive(string $app, string $key, bool $sensitive): bool;
420
+
421
+    /**
422
+     * switch lazy loading status of a config value
423
+     *
424
+     * @param string $app id of the app
425
+     * @param string $key config key
426
+     * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
427
+     *
428
+     * @return bool TRUE if database update was necessary
429
+     * @since 29.0.0
430
+     */
431
+    public function updateLazy(string $app, string $key, bool $lazy): bool;
432
+
433
+    /**
434
+     * returns an array contains details about a config value
435
+     *
436
+     * ```
437
+     * [
438
+     *   "app" => "myapp",
439
+     *   "key" => "mykey",
440
+     *   "value" => "its_value",
441
+     *   "lazy" => false,
442
+     *   "type" => 4,
443
+     *   "typeString" => "string",
444
+     *   'sensitive' => true
445
+     * ]
446
+     * ```
447
+     *
448
+     * @param string $app id of the app
449
+     * @param string $key config key
450
+     *
451
+     * @return array
452
+     * @throws AppConfigUnknownKeyException if config key is not known in database
453
+     * @since 29.0.0
454
+     */
455
+    public function getDetails(string $app, string $key): array;
456
+
457
+    /**
458
+     * returns an array containing details about a config key.
459
+     * key/value pair are available only if it exists.
460
+     *
461
+     * ```
462
+     * [
463
+     *   "app" => "myapp",
464
+     *   "key" => "mykey",
465
+     *   "value" => "current_value",
466
+     *   "default" => "default_if_available",
467
+     *   "definition" => "this is what it does",
468
+     *   "note" => "enabling this is not compatible with that",
469
+     *   "lazy" => false,
470
+     *   "type" => 4,
471
+     *   "typeString" => "string",
472
+     *   'sensitive' => false
473
+     * ]
474
+     * ```
475
+     *
476
+     * @param string $app id of the app
477
+     * @param string $key config key
478
+     *
479
+     * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, internal?: bool, default?: string, definition?: string, note?: string}
480
+     * @since 32.0.0
481
+     */
482
+    public function getKeyDetails(string $app, string $key): array;
483
+
484
+    /**
485
+     * Convert string like 'string', 'integer', 'float', 'bool' or 'array' to
486
+     * to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT},
487
+     * {@see VALUE_BOOL} and {@see VALUE_ARRAY}
488
+     *
489
+     * @param string $type
490
+     *
491
+     * @return int
492
+     * @since 29.0.0
493
+     */
494
+    public function convertTypeToInt(string $type): int;
495
+
496
+    /**
497
+     * Convert bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT},
498
+     * {@see VALUE_BOOL} and {@see VALUE_ARRAY} to human-readable string
499
+     *
500
+     * @param int $type
501
+     *
502
+     * @return string
503
+     * @since 29.0.0
504
+     */
505
+    public function convertTypeToString(int $type): string;
506
+
507
+    /**
508
+     * Delete single config key from database.
509
+     *
510
+     * @param string $app id of the app
511
+     * @param string $key config key
512
+     * @since 29.0.0
513
+     */
514
+    public function deleteKey(string $app, string $key): void;
515
+
516
+    /**
517
+     * delete all config keys linked to an app
518
+     *
519
+     * @param string $app id of the app
520
+     * @since 29.0.0
521
+     */
522
+    public function deleteApp(string $app): void;
523
+
524
+    /**
525
+     * Clear the cache.
526
+     *
527
+     * The cache will be rebuilt only the next time a config value is requested.
528
+     *
529
+     * @param bool $reload set to TRUE to refill cache instantly after clearing it
530
+     * @since 29.0.0
531
+     */
532
+    public function clearCache(bool $reload = false): void;
533
+
534
+    /**
535
+     * get multiply values, either the app or key can be used as wildcard by setting it to false
536
+     *
537
+     * @param string|false $key
538
+     * @param string|false $app
539
+     *
540
+     * @return array|false
541
+     * @since 7.0.0
542
+     * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()}
543
+     */
544
+    public function getValues($app, $key);
545
+
546
+    /**
547
+     * get all values of the app or and filters out sensitive data
548
+     *
549
+     * @param string $app
550
+     *
551
+     * @return array
552
+     * @since 12.0.0
553
+     * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()}
554
+     */
555
+    public function getFilteredValues($app);
556
+
557
+    /**
558
+     * Returns the installed version of all apps
559
+     *
560
+     * @return array<string, string>
561
+     * @since 32.0.0
562
+     */
563
+    public function getAppInstalledVersions(bool $onlyEnabled = false): array;
564 564
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -47,11 +47,11 @@
 block discarded – undo
47 47
 	public const VALUE_ARRAY = 64;
48 48
 
49 49
 	/** @since 31.0.0 */
50
-	public const FLAG_SENSITIVE = 1;   // value is sensitive
50
+	public const FLAG_SENSITIVE = 1; // value is sensitive
51 51
 	/**
52 52
 	 * @since 33.0.0
53 53
 	 */
54
-	public const FLAG_INTERNAL = 4;   // value is considered internal and can be hidden from listing
54
+	public const FLAG_INTERNAL = 4; // value is considered internal and can be hidden from listing
55 55
 
56 56
 	/**
57 57
 	 * Get list of all apps that have at least one config value stored in database
Please login to merge, or discard this patch.
core/Command/Config/App/SetConfig.php 2 patches
Indentation   +228 added lines, -228 removed lines patch added patch discarded remove patch
@@ -19,232 +19,232 @@
 block discarded – undo
19 19
 use Symfony\Component\Console\Question\Question;
20 20
 
21 21
 class SetConfig extends Base {
22
-	protected function configure() {
23
-		parent::configure();
24
-
25
-		$this
26
-			->setName('config:app:set')
27
-			->setDescription('Set an app config value')
28
-			->addArgument(
29
-				'app',
30
-				InputArgument::REQUIRED,
31
-				'Name of the app'
32
-			)
33
-			->addArgument(
34
-				'name',
35
-				InputArgument::REQUIRED,
36
-				'Name of the config to set'
37
-			)
38
-			->addOption(
39
-				'value',
40
-				null,
41
-				InputOption::VALUE_REQUIRED,
42
-				'The new value of the config'
43
-			)
44
-			->addOption(
45
-				'type',
46
-				null,
47
-				InputOption::VALUE_REQUIRED,
48
-				'Value type [string, integer, float, boolean, array]',
49
-				'string'
50
-			)
51
-			->addOption(
52
-				'lazy',
53
-				null,
54
-				InputOption::VALUE_NEGATABLE,
55
-				'Set value as lazy loaded',
56
-			)
57
-			->addOption(
58
-				'sensitive',
59
-				null,
60
-				InputOption::VALUE_NEGATABLE,
61
-				'Set value as sensitive',
62
-			)
63
-			->addOption(
64
-				'internal',
65
-				null,
66
-				InputOption::VALUE_NONE,
67
-				'Confirm the edit of an internal value',
68
-			)
69
-			->addOption(
70
-				'update-only',
71
-				null,
72
-				InputOption::VALUE_NONE,
73
-				'Only updates the value, if it is not set before, it is not being added'
74
-			)
75
-		;
76
-	}
77
-
78
-	protected function execute(InputInterface $input, OutputInterface $output): int {
79
-		$appName = $input->getArgument('app');
80
-		$configName = $input->getArgument('name');
81
-
82
-		if (!($this->appConfig instanceof AppConfig)) {
83
-			throw new \Exception('Only compatible with OC\AppConfig as it uses internal methods');
84
-		}
85
-
86
-		if ($input->hasParameterOption('--update-only') && !$this->appConfig->hasKey($appName, $configName)) {
87
-			$output->writeln(
88
-				'<comment>Config value ' . $configName . ' for app ' . $appName
89
-				. ' not updated, as it has not been set before.</comment>'
90
-			);
91
-
92
-			return 1;
93
-		}
94
-
95
-		$type = $typeString = null;
96
-		if ($input->hasParameterOption('--type')) {
97
-			$typeString = $input->getOption('type');
98
-			$type = $this->appConfig->convertTypeToInt($typeString);
99
-		}
100
-
101
-		/**
102
-		 * If --Value is not specified, returns an exception if no value exists in database
103
-		 * compare with current status in database and displays a reminder that this can break things.
104
-		 * confirmation is required by admin, unless --no-interaction
105
-		 */
106
-		$updated = false;
107
-		if (!$input->hasParameterOption('--value')) {
108
-			if (!$input->getOption('lazy') && $this->appConfig->isLazy($appName, $configName) && $this->ask($input, $output, 'NOT LAZY')) {
109
-				$updated = $this->appConfig->updateLazy($appName, $configName, false);
110
-			}
111
-			if ($input->getOption('lazy') && !$this->appConfig->isLazy($appName, $configName) && $this->ask($input, $output, 'LAZY')) {
112
-				$updated = $this->appConfig->updateLazy($appName, $configName, true) || $updated;
113
-			}
114
-			if (!$input->getOption('sensitive') && $this->appConfig->isSensitive($appName, $configName) && $this->ask($input, $output, 'NOT SENSITIVE')) {
115
-				$updated = $this->appConfig->updateSensitive($appName, $configName, false) || $updated;
116
-			}
117
-			if ($input->getOption('sensitive') && !$this->appConfig->isSensitive($appName, $configName) && $this->ask($input, $output, 'SENSITIVE')) {
118
-				$updated = $this->appConfig->updateSensitive($appName, $configName, true) || $updated;
119
-			}
120
-			if ($type !== null && $type !== $this->appConfig->getValueType($appName, $configName) && $typeString !== null && $this->ask($input, $output, $typeString)) {
121
-				$updated = $this->appConfig->updateType($appName, $configName, $type) || $updated;
122
-			}
123
-		} else {
124
-			/**
125
-			 * If --type is specified in the command line, we upgrade the type in database
126
-			 * after a confirmation from admin.
127
-			 * If not we get the type from current stored value or VALUE_MIXED as default.
128
-			 */
129
-			try {
130
-				$currType = $this->appConfig->getValueType($appName, $configName);
131
-				if ($type === null || $typeString === null || $type === $currType || !$this->ask($input, $output, $typeString)) {
132
-					$type = $currType;
133
-				} else {
134
-					$updated = $this->appConfig->updateType($appName, $configName, $type);
135
-				}
136
-			} catch (AppConfigUnknownKeyException) {
137
-				$type = $type ?? IAppConfig::VALUE_MIXED;
138
-			}
139
-
140
-			/**
141
-			 * if --lazy/--no-lazy option are set, compare with data stored in database.
142
-			 * If no data in database, or identical, continue.
143
-			 * If different, ask admin for confirmation.
144
-			 */
145
-			$lazy = $input->getOption('lazy');
146
-			try {
147
-				$currLazy = $this->appConfig->isLazy($appName, $configName);
148
-				if ($lazy === null || $lazy === $currLazy || !$this->ask($input, $output, ($lazy) ? 'LAZY' : 'NOT LAZY')) {
149
-					$lazy = $currLazy;
150
-				}
151
-			} catch (AppConfigUnknownKeyException) {
152
-				$lazy = $lazy ?? false;
153
-			}
154
-
155
-			/**
156
-			 * same with sensitive status
157
-			 */
158
-			$sensitive = $input->getOption('sensitive');
159
-			try {
160
-				$currSensitive = $this->appConfig->isSensitive($appName, $configName, null);
161
-				if ($sensitive === null || $sensitive === $currSensitive || !$this->ask($input, $output, ($sensitive) ? 'SENSITIVE' : 'NOT SENSITIVE')) {
162
-					$sensitive = $currSensitive;
163
-				}
164
-			} catch (AppConfigUnknownKeyException) {
165
-				$sensitive = $sensitive ?? false;
166
-			}
167
-
168
-			if (!$input->getOption('internal') && ($this->appConfig->getKeyDetails($appName, $configName)['internal'] ?? false)) {
169
-				$output->writeln('<error>Config key is set as INTERNAL and modifying it might induce strange behavior or break user experience.</error>');
170
-				$output->writeln('please use option <comment>--internal</comment> to confirm your action');
171
-				return self::FAILURE;
172
-			}
173
-
174
-			$value = (string)$input->getOption('value');
175
-			switch ($type) {
176
-				case IAppConfig::VALUE_MIXED:
177
-					$updated = $this->appConfig->setValueMixed($appName, $configName, $value, $lazy, $sensitive);
178
-					break;
179
-
180
-				case IAppConfig::VALUE_STRING:
181
-					$updated = $this->appConfig->setValueString($appName, $configName, $value, $lazy, $sensitive);
182
-					break;
183
-
184
-				case IAppConfig::VALUE_INT:
185
-					$updated = $this->appConfig->setValueInt($appName, $configName, $this->configManager->convertToInt($value), $lazy, $sensitive);
186
-					break;
187
-
188
-				case IAppConfig::VALUE_FLOAT:
189
-					$updated = $this->appConfig->setValueFloat($appName, $configName, $this->configManager->convertToFloat($value), $lazy, $sensitive);
190
-					break;
191
-
192
-				case IAppConfig::VALUE_BOOL:
193
-					$updated = $this->appConfig->setValueBool($appName, $configName, $this->configManager->convertToBool($value), $lazy);
194
-					break;
195
-
196
-				case IAppConfig::VALUE_ARRAY:
197
-					$updated = $this->appConfig->setValueArray($appName, $configName, $this->configManager->convertToArray($value), $lazy, $sensitive);
198
-					break;
199
-			}
200
-		}
201
-
202
-		if ($updated) {
203
-			$current = $this->appConfig->getDetails($appName, $configName);
204
-			$output->writeln(
205
-				sprintf(
206
-					"<info>Config value '%s' for app '%s' is now set to '%s', stored as %s in %s</info>",
207
-					$configName,
208
-					$appName,
209
-					$current['sensitive'] ? '<sensitive>' : $current['value'],
210
-					$current['typeString'],
211
-					$current['lazy'] ? 'lazy cache' : 'fast cache'
212
-				)
213
-			);
214
-			$keyDetails = $this->appConfig->getKeyDetails($appName, $configName);
215
-			if (($keyDetails['note'] ?? '') !== '') {
216
-				$output->writeln('<comment>Note:</comment> ' . $keyDetails['note']);
217
-			}
218
-
219
-		} else {
220
-			$output->writeln('<info>Config value were not updated</info>');
221
-		}
222
-
223
-		return 0;
224
-	}
225
-
226
-	private function ask(InputInterface $input, OutputInterface $output, string $request): bool {
227
-		/** @var QuestionHelper $helper */
228
-		$helper = $this->getHelper('question');
229
-		if ($input->getOption('no-interaction')) {
230
-			return true;
231
-		}
232
-
233
-		$output->writeln(sprintf('You are about to set config value %s as <info>%s</info>',
234
-			'<info>' . $input->getArgument('app') . '</info>/<info>' . $input->getArgument('name') . '</info>',
235
-			strtoupper($request)
236
-		));
237
-		$output->writeln('');
238
-		$output->writeln('<comment>This might break thing, affect performance on your instance or its security!</comment>');
239
-
240
-		$result = (strtolower((string)$helper->ask(
241
-			$input,
242
-			$output,
243
-			new Question('<comment>Confirm this action by typing \'yes\'</comment>: '))) === 'yes');
244
-
245
-		$output->writeln(($result) ? 'done' : 'cancelled');
246
-		$output->writeln('');
247
-
248
-		return $result;
249
-	}
22
+    protected function configure() {
23
+        parent::configure();
24
+
25
+        $this
26
+            ->setName('config:app:set')
27
+            ->setDescription('Set an app config value')
28
+            ->addArgument(
29
+                'app',
30
+                InputArgument::REQUIRED,
31
+                'Name of the app'
32
+            )
33
+            ->addArgument(
34
+                'name',
35
+                InputArgument::REQUIRED,
36
+                'Name of the config to set'
37
+            )
38
+            ->addOption(
39
+                'value',
40
+                null,
41
+                InputOption::VALUE_REQUIRED,
42
+                'The new value of the config'
43
+            )
44
+            ->addOption(
45
+                'type',
46
+                null,
47
+                InputOption::VALUE_REQUIRED,
48
+                'Value type [string, integer, float, boolean, array]',
49
+                'string'
50
+            )
51
+            ->addOption(
52
+                'lazy',
53
+                null,
54
+                InputOption::VALUE_NEGATABLE,
55
+                'Set value as lazy loaded',
56
+            )
57
+            ->addOption(
58
+                'sensitive',
59
+                null,
60
+                InputOption::VALUE_NEGATABLE,
61
+                'Set value as sensitive',
62
+            )
63
+            ->addOption(
64
+                'internal',
65
+                null,
66
+                InputOption::VALUE_NONE,
67
+                'Confirm the edit of an internal value',
68
+            )
69
+            ->addOption(
70
+                'update-only',
71
+                null,
72
+                InputOption::VALUE_NONE,
73
+                'Only updates the value, if it is not set before, it is not being added'
74
+            )
75
+        ;
76
+    }
77
+
78
+    protected function execute(InputInterface $input, OutputInterface $output): int {
79
+        $appName = $input->getArgument('app');
80
+        $configName = $input->getArgument('name');
81
+
82
+        if (!($this->appConfig instanceof AppConfig)) {
83
+            throw new \Exception('Only compatible with OC\AppConfig as it uses internal methods');
84
+        }
85
+
86
+        if ($input->hasParameterOption('--update-only') && !$this->appConfig->hasKey($appName, $configName)) {
87
+            $output->writeln(
88
+                '<comment>Config value ' . $configName . ' for app ' . $appName
89
+                . ' not updated, as it has not been set before.</comment>'
90
+            );
91
+
92
+            return 1;
93
+        }
94
+
95
+        $type = $typeString = null;
96
+        if ($input->hasParameterOption('--type')) {
97
+            $typeString = $input->getOption('type');
98
+            $type = $this->appConfig->convertTypeToInt($typeString);
99
+        }
100
+
101
+        /**
102
+         * If --Value is not specified, returns an exception if no value exists in database
103
+         * compare with current status in database and displays a reminder that this can break things.
104
+         * confirmation is required by admin, unless --no-interaction
105
+         */
106
+        $updated = false;
107
+        if (!$input->hasParameterOption('--value')) {
108
+            if (!$input->getOption('lazy') && $this->appConfig->isLazy($appName, $configName) && $this->ask($input, $output, 'NOT LAZY')) {
109
+                $updated = $this->appConfig->updateLazy($appName, $configName, false);
110
+            }
111
+            if ($input->getOption('lazy') && !$this->appConfig->isLazy($appName, $configName) && $this->ask($input, $output, 'LAZY')) {
112
+                $updated = $this->appConfig->updateLazy($appName, $configName, true) || $updated;
113
+            }
114
+            if (!$input->getOption('sensitive') && $this->appConfig->isSensitive($appName, $configName) && $this->ask($input, $output, 'NOT SENSITIVE')) {
115
+                $updated = $this->appConfig->updateSensitive($appName, $configName, false) || $updated;
116
+            }
117
+            if ($input->getOption('sensitive') && !$this->appConfig->isSensitive($appName, $configName) && $this->ask($input, $output, 'SENSITIVE')) {
118
+                $updated = $this->appConfig->updateSensitive($appName, $configName, true) || $updated;
119
+            }
120
+            if ($type !== null && $type !== $this->appConfig->getValueType($appName, $configName) && $typeString !== null && $this->ask($input, $output, $typeString)) {
121
+                $updated = $this->appConfig->updateType($appName, $configName, $type) || $updated;
122
+            }
123
+        } else {
124
+            /**
125
+             * If --type is specified in the command line, we upgrade the type in database
126
+             * after a confirmation from admin.
127
+             * If not we get the type from current stored value or VALUE_MIXED as default.
128
+             */
129
+            try {
130
+                $currType = $this->appConfig->getValueType($appName, $configName);
131
+                if ($type === null || $typeString === null || $type === $currType || !$this->ask($input, $output, $typeString)) {
132
+                    $type = $currType;
133
+                } else {
134
+                    $updated = $this->appConfig->updateType($appName, $configName, $type);
135
+                }
136
+            } catch (AppConfigUnknownKeyException) {
137
+                $type = $type ?? IAppConfig::VALUE_MIXED;
138
+            }
139
+
140
+            /**
141
+             * if --lazy/--no-lazy option are set, compare with data stored in database.
142
+             * If no data in database, or identical, continue.
143
+             * If different, ask admin for confirmation.
144
+             */
145
+            $lazy = $input->getOption('lazy');
146
+            try {
147
+                $currLazy = $this->appConfig->isLazy($appName, $configName);
148
+                if ($lazy === null || $lazy === $currLazy || !$this->ask($input, $output, ($lazy) ? 'LAZY' : 'NOT LAZY')) {
149
+                    $lazy = $currLazy;
150
+                }
151
+            } catch (AppConfigUnknownKeyException) {
152
+                $lazy = $lazy ?? false;
153
+            }
154
+
155
+            /**
156
+             * same with sensitive status
157
+             */
158
+            $sensitive = $input->getOption('sensitive');
159
+            try {
160
+                $currSensitive = $this->appConfig->isSensitive($appName, $configName, null);
161
+                if ($sensitive === null || $sensitive === $currSensitive || !$this->ask($input, $output, ($sensitive) ? 'SENSITIVE' : 'NOT SENSITIVE')) {
162
+                    $sensitive = $currSensitive;
163
+                }
164
+            } catch (AppConfigUnknownKeyException) {
165
+                $sensitive = $sensitive ?? false;
166
+            }
167
+
168
+            if (!$input->getOption('internal') && ($this->appConfig->getKeyDetails($appName, $configName)['internal'] ?? false)) {
169
+                $output->writeln('<error>Config key is set as INTERNAL and modifying it might induce strange behavior or break user experience.</error>');
170
+                $output->writeln('please use option <comment>--internal</comment> to confirm your action');
171
+                return self::FAILURE;
172
+            }
173
+
174
+            $value = (string)$input->getOption('value');
175
+            switch ($type) {
176
+                case IAppConfig::VALUE_MIXED:
177
+                    $updated = $this->appConfig->setValueMixed($appName, $configName, $value, $lazy, $sensitive);
178
+                    break;
179
+
180
+                case IAppConfig::VALUE_STRING:
181
+                    $updated = $this->appConfig->setValueString($appName, $configName, $value, $lazy, $sensitive);
182
+                    break;
183
+
184
+                case IAppConfig::VALUE_INT:
185
+                    $updated = $this->appConfig->setValueInt($appName, $configName, $this->configManager->convertToInt($value), $lazy, $sensitive);
186
+                    break;
187
+
188
+                case IAppConfig::VALUE_FLOAT:
189
+                    $updated = $this->appConfig->setValueFloat($appName, $configName, $this->configManager->convertToFloat($value), $lazy, $sensitive);
190
+                    break;
191
+
192
+                case IAppConfig::VALUE_BOOL:
193
+                    $updated = $this->appConfig->setValueBool($appName, $configName, $this->configManager->convertToBool($value), $lazy);
194
+                    break;
195
+
196
+                case IAppConfig::VALUE_ARRAY:
197
+                    $updated = $this->appConfig->setValueArray($appName, $configName, $this->configManager->convertToArray($value), $lazy, $sensitive);
198
+                    break;
199
+            }
200
+        }
201
+
202
+        if ($updated) {
203
+            $current = $this->appConfig->getDetails($appName, $configName);
204
+            $output->writeln(
205
+                sprintf(
206
+                    "<info>Config value '%s' for app '%s' is now set to '%s', stored as %s in %s</info>",
207
+                    $configName,
208
+                    $appName,
209
+                    $current['sensitive'] ? '<sensitive>' : $current['value'],
210
+                    $current['typeString'],
211
+                    $current['lazy'] ? 'lazy cache' : 'fast cache'
212
+                )
213
+            );
214
+            $keyDetails = $this->appConfig->getKeyDetails($appName, $configName);
215
+            if (($keyDetails['note'] ?? '') !== '') {
216
+                $output->writeln('<comment>Note:</comment> ' . $keyDetails['note']);
217
+            }
218
+
219
+        } else {
220
+            $output->writeln('<info>Config value were not updated</info>');
221
+        }
222
+
223
+        return 0;
224
+    }
225
+
226
+    private function ask(InputInterface $input, OutputInterface $output, string $request): bool {
227
+        /** @var QuestionHelper $helper */
228
+        $helper = $this->getHelper('question');
229
+        if ($input->getOption('no-interaction')) {
230
+            return true;
231
+        }
232
+
233
+        $output->writeln(sprintf('You are about to set config value %s as <info>%s</info>',
234
+            '<info>' . $input->getArgument('app') . '</info>/<info>' . $input->getArgument('name') . '</info>',
235
+            strtoupper($request)
236
+        ));
237
+        $output->writeln('');
238
+        $output->writeln('<comment>This might break thing, affect performance on your instance or its security!</comment>');
239
+
240
+        $result = (strtolower((string)$helper->ask(
241
+            $input,
242
+            $output,
243
+            new Question('<comment>Confirm this action by typing \'yes\'</comment>: '))) === 'yes');
244
+
245
+        $output->writeln(($result) ? 'done' : 'cancelled');
246
+        $output->writeln('');
247
+
248
+        return $result;
249
+    }
250 250
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
 
86 86
 		if ($input->hasParameterOption('--update-only') && !$this->appConfig->hasKey($appName, $configName)) {
87 87
 			$output->writeln(
88
-				'<comment>Config value ' . $configName . ' for app ' . $appName
88
+				'<comment>Config value '.$configName.' for app '.$appName
89 89
 				. ' not updated, as it has not been set before.</comment>'
90 90
 			);
91 91
 
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 				return self::FAILURE;
172 172
 			}
173 173
 
174
-			$value = (string)$input->getOption('value');
174
+			$value = (string) $input->getOption('value');
175 175
 			switch ($type) {
176 176
 				case IAppConfig::VALUE_MIXED:
177 177
 					$updated = $this->appConfig->setValueMixed($appName, $configName, $value, $lazy, $sensitive);
@@ -213,7 +213,7 @@  discard block
 block discarded – undo
213 213
 			);
214 214
 			$keyDetails = $this->appConfig->getKeyDetails($appName, $configName);
215 215
 			if (($keyDetails['note'] ?? '') !== '') {
216
-				$output->writeln('<comment>Note:</comment> ' . $keyDetails['note']);
216
+				$output->writeln('<comment>Note:</comment> '.$keyDetails['note']);
217 217
 			}
218 218
 
219 219
 		} else {
@@ -231,13 +231,13 @@  discard block
 block discarded – undo
231 231
 		}
232 232
 
233 233
 		$output->writeln(sprintf('You are about to set config value %s as <info>%s</info>',
234
-			'<info>' . $input->getArgument('app') . '</info>/<info>' . $input->getArgument('name') . '</info>',
234
+			'<info>'.$input->getArgument('app').'</info>/<info>'.$input->getArgument('name').'</info>',
235 235
 			strtoupper($request)
236 236
 		));
237 237
 		$output->writeln('');
238 238
 		$output->writeln('<comment>This might break thing, affect performance on your instance or its security!</comment>');
239 239
 
240
-		$result = (strtolower((string)$helper->ask(
240
+		$result = (strtolower((string) $helper->ask(
241 241
 			$input,
242 242
 			$output,
243 243
 			new Question('<comment>Confirm this action by typing \'yes\'</comment>: '))) === 'yes');
Please login to merge, or discard this patch.