2023-01-01 20:23:53 +00:00
|
|
|
<?php
|
|
|
|
namespace Misuzu\Config;
|
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
use RuntimeException;
|
|
|
|
use InvalidArgumentException;
|
2023-01-01 20:23:53 +00:00
|
|
|
use Index\Data\DataException;
|
|
|
|
use Index\Data\IDbConnection;
|
|
|
|
use Index\Data\IDbStatement;
|
2023-07-18 21:48:44 +00:00
|
|
|
use Index\Data\IDbResult;
|
|
|
|
use Misuzu\DbStatementCache;
|
|
|
|
use Misuzu\Pagination;
|
2023-01-01 20:23:53 +00:00
|
|
|
|
|
|
|
class DbConfig implements IConfig {
|
2023-07-18 21:48:44 +00:00
|
|
|
private DbStatementCache $cache;
|
2023-01-01 20:23:53 +00:00
|
|
|
private array $values = [];
|
|
|
|
|
|
|
|
public function __construct(IDbConnection $dbConn) {
|
2023-07-18 21:48:44 +00:00
|
|
|
$this->cache = new DbStatementCache($dbConn);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function validateName(string $name): bool {
|
|
|
|
// this should better validate the format, this allows for a lot of shittery
|
|
|
|
return preg_match('#^([a-z][a-zA-Z0-9._]+)$#', $name) === 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function reset(): void {
|
2023-01-01 20:23:53 +00:00
|
|
|
$this->values = [];
|
2023-07-18 21:48:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function unload(string|array $names): void {
|
|
|
|
if(empty($names))
|
|
|
|
return;
|
|
|
|
if(is_string($names))
|
|
|
|
$names = [$names];
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
foreach($names as $name)
|
|
|
|
unset($this->values[$name]);
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function scopeTo(string $prefix): IConfig {
|
|
|
|
return new ScopedConfig($this, $prefix);
|
|
|
|
}
|
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
public function hasValues(string|array $names): bool {
|
|
|
|
if(empty($names))
|
|
|
|
return true;
|
|
|
|
if(is_string($names))
|
|
|
|
$names = [$names];
|
|
|
|
|
|
|
|
$cachedNames = array_keys($this->values);
|
|
|
|
$names = array_diff($names, $cachedNames);
|
|
|
|
|
|
|
|
if(!empty($names)) {
|
|
|
|
// array_diff preserves keys, the for() later would fuck up without it
|
|
|
|
$names = array_values($names);
|
|
|
|
$nameCount = count($names);
|
|
|
|
|
|
|
|
$stmt = $this->cache->get(sprintf(
|
|
|
|
'SELECT COUNT(*) FROM msz_config WHERE config_name IN (%s)',
|
2023-07-20 19:36:43 +00:00
|
|
|
msz_where_in_list($nameCount)
|
2023-07-18 21:48:44 +00:00
|
|
|
));
|
|
|
|
for($i = 0; $i < $nameCount; ++$i)
|
|
|
|
$stmt->addParameter($i + 1, $names[$i]);
|
|
|
|
$stmt->execute();
|
|
|
|
|
|
|
|
$result = $stmt->getResult();
|
|
|
|
if($result->next())
|
|
|
|
return $result->getInteger(0) >= $nameCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function removeValues(string|array $names): void {
|
|
|
|
if(empty($names))
|
|
|
|
return;
|
|
|
|
if(is_string($names))
|
|
|
|
$names = [$names];
|
|
|
|
|
|
|
|
foreach($names as $name)
|
|
|
|
unset($this->values[$name]);
|
|
|
|
|
|
|
|
$nameCount = count($names);
|
|
|
|
$stmt = $this->cache->get(sprintf(
|
|
|
|
'DELETE FROM msz_config WHERE config_name IN (%s)',
|
2023-07-20 19:36:43 +00:00
|
|
|
msz_where_in_list($nameCount)
|
2023-07-18 21:48:44 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
for($i = 0; $i < $nameCount; ++$i)
|
|
|
|
$stmt->addParameter($i + 1, $names[$i]);
|
|
|
|
|
|
|
|
$stmt->execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAllValueInfos(?Pagination $pagination = null): array {
|
|
|
|
$this->reset();
|
|
|
|
$infos = [];
|
|
|
|
|
|
|
|
$hasPagination = $pagination !== null;
|
|
|
|
|
|
|
|
$query = 'SELECT config_name, config_value FROM msz_config';
|
|
|
|
if($hasPagination)
|
|
|
|
$query .= ' LIMIT ? RANGE ?';
|
|
|
|
|
|
|
|
$stmt = $this->cache->get($query);
|
|
|
|
if($hasPagination) {
|
|
|
|
$stmt->addParameter(1, $pagination->getRange());
|
|
|
|
$stmt->addParameter(2, $pagination->getOffset());
|
|
|
|
}
|
|
|
|
$stmt->execute();
|
|
|
|
|
|
|
|
$result = $stmt->getResult();
|
|
|
|
|
|
|
|
while($result->next()) {
|
|
|
|
$name = $result->getString(0);
|
|
|
|
$infos[] = $this->values[$name] = new DbConfigValueInfo($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $infos;
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
public function getValueInfos(string|array $names): array {
|
|
|
|
if(empty($names))
|
|
|
|
return [];
|
|
|
|
if(is_string($names))
|
|
|
|
$names = [$names];
|
|
|
|
|
|
|
|
$infos = [];
|
|
|
|
$skip = [];
|
|
|
|
|
|
|
|
foreach($names as $name)
|
|
|
|
if(array_key_exists($name, $this->values)) {
|
|
|
|
$infos[] = $this->values[$name];
|
|
|
|
$skip[] = $name;
|
|
|
|
}
|
|
|
|
|
|
|
|
$names = array_diff($names, $skip);
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
if(!empty($names)) {
|
|
|
|
// array_diff preserves keys, the for() later would fuck up without it
|
|
|
|
$names = array_values($names);
|
|
|
|
$nameCount = count($names);
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
$stmt = $this->cache->get(sprintf(
|
|
|
|
'SELECT config_name, config_value FROM msz_config WHERE config_name IN (%s)',
|
2023-07-20 19:36:43 +00:00
|
|
|
msz_where_in_list($nameCount)
|
2023-07-18 21:48:44 +00:00
|
|
|
));
|
|
|
|
for($i = 0; $i < $nameCount; ++$i)
|
|
|
|
$stmt->addParameter($i + 1, $names[$i]);
|
|
|
|
$stmt->execute();
|
|
|
|
|
|
|
|
$result = $stmt->getResult();
|
|
|
|
while($result->next()) {
|
|
|
|
$name = $result->getString(0);
|
|
|
|
$infos[] = $this->values[$name] = new DbConfigValueInfo($result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $infos;
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
public function getValueInfo(string $name): ?IConfigValueInfo {
|
|
|
|
$infos = $this->getValueInfos($name);
|
|
|
|
return empty($infos) ? null : $infos[0];
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
public function getValues(array $specs): array {
|
|
|
|
$names = [];
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
foreach($specs as $key => $spec) {
|
|
|
|
if(is_string($spec)) {
|
|
|
|
$name = $spec;
|
|
|
|
$default = null;
|
|
|
|
} elseif(is_array($spec) && !empty($spec)) {
|
|
|
|
$name = $spec[0];
|
|
|
|
$default = $spec[1] ?? null;
|
|
|
|
} else
|
|
|
|
throw new InvalidArgumentException('$specs array contains an invalid entry.');
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
if(($colon = strpos($name, ':')) !== false) {
|
|
|
|
$type = substr($name, $colon + 1, 1);
|
|
|
|
$name = substr($name, 0, $colon);
|
|
|
|
} else $type = '';
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
$names[] = $name;
|
|
|
|
$specs[$key] = [
|
|
|
|
'name' => $name,
|
|
|
|
'type' => $type,
|
|
|
|
'default' => $default,
|
|
|
|
];
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
2023-07-18 21:48:44 +00:00
|
|
|
|
|
|
|
$infos = $this->getValueInfos($names);
|
|
|
|
$results = [];
|
|
|
|
|
|
|
|
foreach($specs as $spec) {
|
|
|
|
foreach($infos as $infoTest)
|
|
|
|
if($infoTest->getName() === $spec['name']) {
|
|
|
|
$info = $infoTest;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!isset($info)) {
|
|
|
|
$defaultValue = $spec['default'] ?? null;
|
|
|
|
if($spec['type'] !== '')
|
|
|
|
settype($defaultValue, match($spec['type']) {
|
|
|
|
's' => 'string',
|
|
|
|
'a' => 'array',
|
|
|
|
'i' => 'int',
|
|
|
|
'b' => 'bool',
|
|
|
|
'f' => 'float',
|
|
|
|
'd' => 'double',
|
|
|
|
default => throw new RuntimeException('Invalid type letter encountered.'),
|
|
|
|
});
|
|
|
|
|
|
|
|
$results[$spec['name']] = $defaultValue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$results[$spec['name']] = match($spec['type']) {
|
|
|
|
's' => $info->getString(),
|
|
|
|
'a' => $info->getArray(),
|
|
|
|
'i' => $info->getInteger(),
|
|
|
|
'b' => $info->getBoolean(),
|
|
|
|
'f' => $info->getFloat(),
|
|
|
|
'd' => $info->getFloat(),
|
|
|
|
'' => $info->getValue(),
|
|
|
|
default => throw new RuntimeException('Unknown type encountered in $specs.'),
|
|
|
|
};
|
|
|
|
|
|
|
|
unset($info);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getString(string $name, string $default = ''): string {
|
|
|
|
$valueInfo = $this->getValueInfo($name);
|
|
|
|
return $valueInfo?->isString() ? $valueInfo->getString() : $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getInteger(string $name, int $default = 0): int {
|
|
|
|
$valueInfo = $this->getValueInfo($name);
|
|
|
|
return $valueInfo?->isInteger() ? $valueInfo->getInteger() : $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getFloat(string $name, float $default = 0): float {
|
|
|
|
$valueInfo = $this->getValueInfo($name);
|
|
|
|
return $valueInfo?->isFloat() ? $valueInfo->getFloat() : $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBoolean(string $name, bool $default = false): bool {
|
|
|
|
$valueInfo = $this->getValueInfo($name);
|
|
|
|
return $valueInfo?->isBoolean() ? $valueInfo->getBoolean() : $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getArray(string $name, array $default = []): array {
|
|
|
|
$valueInfo = $this->getValueInfo($name);
|
|
|
|
return $valueInfo?->isArray() ? $valueInfo->getArray() : $default;
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
public function setValues(array $values): void {
|
|
|
|
if(empty($values))
|
|
|
|
return;
|
|
|
|
|
|
|
|
$valueCount = count($values);
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
$stmt = $this->cache->get(sprintf(
|
|
|
|
'REPLACE INTO msz_config (config_name, config_value) VALUES %s',
|
2023-07-20 19:36:43 +00:00
|
|
|
msz_where_in_list($valueCount, '(?, ?)')
|
2023-07-18 21:48:44 +00:00
|
|
|
));
|
2023-01-01 20:23:53 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
$args = 0;
|
|
|
|
foreach($values as $name => $value) {
|
|
|
|
if(!self::validateName($name))
|
|
|
|
throw new InvalidArgumentException('Invalid name encountered in $values.');
|
|
|
|
|
|
|
|
if(is_array($value)) {
|
|
|
|
foreach($value as $entry)
|
|
|
|
if(!is_scalar($entry))
|
|
|
|
throw new InvalidArgumentException('An array value in $values contains a non-scalar type.');
|
|
|
|
} elseif(!is_scalar($value))
|
|
|
|
throw new InvalidArgumentException('Invalid value type encountered in $values.');
|
|
|
|
|
|
|
|
$stmt->addParameter(++$args, $name);
|
|
|
|
$stmt->addParameter(++$args, serialize($value));
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
2023-07-18 21:48:44 +00:00
|
|
|
|
|
|
|
$stmt->execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setString(string $name, string $value): void {
|
|
|
|
$this->setValues([$name => $value]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setInteger(string $name, int $value): void {
|
|
|
|
$this->setValues([$name => $value]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setFloat(string $name, float $value): void {
|
|
|
|
$this->setValues([$name => $value]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setBoolean(string $name, bool $value): void {
|
|
|
|
$this->setValues([$name => $value]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setArray(string $name, array $value): void {
|
|
|
|
$this->setValues([$name => $value]);
|
2023-01-01 20:23:53 +00:00
|
|
|
}
|
|
|
|
}
|