Imported Syokuhou into Index.
This commit is contained in:
parent
eb0f65c5ef
commit
28d65263be
18 changed files with 1924 additions and 1 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.2410.42042
|
||||
0.2410.42227
|
||||
|
|
192
src/Config/Config.php
Normal file
192
src/Config/Config.php
Normal file
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
// Config.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Provides a common interface for configuration providers.
|
||||
*/
|
||||
interface Config {
|
||||
/**
|
||||
* Creates a scoped configuration instance that prepends a prefix to all names.
|
||||
*
|
||||
* @param non-empty-array<string> ...$prefix Parts of the desired.
|
||||
* @return Config A scoped configuration instance.
|
||||
*/
|
||||
public function scopeTo(string ...$prefix): Config;
|
||||
|
||||
/**
|
||||
* Gets the separator character used for scoping.
|
||||
*
|
||||
* @return string Separator character.
|
||||
*/
|
||||
public function getSeparator(): string;
|
||||
|
||||
/**
|
||||
* Checks if configurations contains given names.
|
||||
* An empty $names list will always return true.
|
||||
*
|
||||
* @param string|string[] $names Name or names to check for.
|
||||
* @return bool Whether all given names are present or not.
|
||||
*/
|
||||
public function hasValues(string|array $names): bool;
|
||||
|
||||
/**
|
||||
* Removes values with given names from the configuration, if writable.
|
||||
*
|
||||
* @param string|string[] $names Name or names to remove.
|
||||
* @throws RuntimeException If the configuration is read only.
|
||||
*/
|
||||
public function removeValues(string|array $names): void;
|
||||
|
||||
/**
|
||||
* Lists all value informations from this configuration.
|
||||
*
|
||||
* @param int $range Amount of items to take, 0 for all. Must be a positive integer.
|
||||
* @param int $offset Amount of items to skip. Must be a positive integer. Has no effect if $range is 0.
|
||||
* @throws InvalidArgumentException If $range or $offset are negative.
|
||||
* @return ConfigValueInfo[] Configuration value infos.
|
||||
*/
|
||||
public function getAllValueInfos(int $range = 0, int $offset = 0): array;
|
||||
|
||||
/**
|
||||
* Gets value informations for one or more items.
|
||||
*
|
||||
* @param string|string[] $names Name or names to retrieve.
|
||||
* @return ConfigValueInfo[] Array with value informations.
|
||||
*/
|
||||
public function getValueInfos(string|array $names): array;
|
||||
|
||||
/**
|
||||
* Gets value information for a single item.
|
||||
*
|
||||
* @param string $name Name or names to retrieve.
|
||||
* @return ?ConfigValueInfo Value information, or null if not present.
|
||||
*/
|
||||
public function getValueInfo(string $name): ?ConfigValueInfo;
|
||||
|
||||
/**
|
||||
* Gets multiple values of varying types at once.
|
||||
*
|
||||
* The format of a $specs entry can be one of the following:
|
||||
* If the entry is a string:
|
||||
* - The name of a value: 'value_name'
|
||||
* - The name of a value followed by a requested type, separated by a colon: 'value_name:s', 'value_name:i', 'value_name:a'
|
||||
* If the entry is an array, all except [0] are optional:
|
||||
* - [0] follows to same format as the above described string
|
||||
* - [1] is the default value to fall back on, MUST be the same type as the one specified in [0].
|
||||
* - [2] is an alternative key for the output array.
|
||||
*
|
||||
* Available types are:
|
||||
* :s - string
|
||||
* :a - array
|
||||
* :i - integer
|
||||
* :b - boolean
|
||||
* :f - float
|
||||
* :d - float
|
||||
*
|
||||
* @param array<string|string[]> $specs Specification of what items to grab.
|
||||
* @throws InvalidArgumentException If $specs is malformed.
|
||||
* @return array<string, mixed> An associative array containing the retrieved values.
|
||||
*/
|
||||
public function getValues(array $specs): array;
|
||||
|
||||
/**
|
||||
* Gets a single string value.
|
||||
*
|
||||
* @param string $name Name of the value to fetch.
|
||||
* @param string $default Default value to fall back on if the value is not present.
|
||||
* @return string Configuration value for $name.
|
||||
*/
|
||||
public function getString(string $name, string $default = ''): string;
|
||||
|
||||
/**
|
||||
* Gets a single integer value.
|
||||
*
|
||||
* @param string $name Name of the value to fetch.
|
||||
* @param int $default Default value to fall back on if the value is not present.
|
||||
* @return int Configuration value for $name.
|
||||
*/
|
||||
public function getInteger(string $name, int $default = 0): int;
|
||||
|
||||
/**
|
||||
* Gets a single floating point value.
|
||||
*
|
||||
* @param string $name Name of the value to fetch.
|
||||
* @param float $default Default value to fall back on if the value is not present.
|
||||
* @return float Configuration value for $name.
|
||||
*/
|
||||
public function getFloat(string $name, float $default = 0): float;
|
||||
|
||||
/**
|
||||
* Gets a single boolean value.
|
||||
*
|
||||
* @param string $name Name of the value to fetch.
|
||||
* @param bool $default Default value to fall back on if the value is not present.
|
||||
* @return bool Configuration value for $name.
|
||||
*/
|
||||
public function getBoolean(string $name, bool $default = false): bool;
|
||||
|
||||
/**
|
||||
* Gets an array value.
|
||||
*
|
||||
* @param string $name Name of the value to fetch.
|
||||
* @param mixed[] $default Default value to fall back on if the value is not present.
|
||||
* @return mixed[] Configuration value for $name.
|
||||
*/
|
||||
public function getArray(string $name, array $default = []): array;
|
||||
|
||||
/**
|
||||
* Sets multiple values at once using an associative array.
|
||||
*
|
||||
* @param array<string, mixed> $values Values to save.
|
||||
* @throws InvalidArgumentException If $values is malformed.
|
||||
* @throws RuntimeException If the configuration is read only.
|
||||
*/
|
||||
public function setValues(array $values): void;
|
||||
|
||||
/**
|
||||
* Sets a single string value.
|
||||
*
|
||||
* @param string $name Name of the value to save.
|
||||
* @param string $value Value to save.
|
||||
*/
|
||||
public function setString(string $name, string $value): void;
|
||||
|
||||
/**
|
||||
* Sets a single integer value.
|
||||
*
|
||||
* @param string $name Name of the value to save.
|
||||
* @param int $value Value to save.
|
||||
*/
|
||||
public function setInteger(string $name, int $value): void;
|
||||
|
||||
/**
|
||||
* Sets a single floating point value.
|
||||
*
|
||||
* @param string $name Name of the value to save.
|
||||
* @param float $value Value to save.
|
||||
*/
|
||||
public function setFloat(string $name, float $value): void;
|
||||
|
||||
/**
|
||||
* Sets a single boolean value.
|
||||
*
|
||||
* @param string $name Name of the value to save.
|
||||
* @param bool $value Value to save.
|
||||
*/
|
||||
public function setBoolean(string $name, bool $value): void;
|
||||
|
||||
/**
|
||||
* Sets an array value.
|
||||
*
|
||||
* @param string $name Name of the value to save.
|
||||
* @param mixed[] $value Value to save.
|
||||
*/
|
||||
public function setArray(string $name, array $value): void;
|
||||
}
|
110
src/Config/ConfigValueInfo.php
Normal file
110
src/Config/ConfigValueInfo.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
// ConfigValueInfo.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
use Stringable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Provides a common interface for configuration values.
|
||||
*/
|
||||
interface ConfigValueInfo extends Stringable {
|
||||
/**
|
||||
* Gets the name of this configuration value.
|
||||
*
|
||||
* @return string Configuration value name.
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Gets the type of this configuration value.
|
||||
*
|
||||
* @return string Configuration value type name.
|
||||
*/
|
||||
public function getType(): string;
|
||||
|
||||
/**
|
||||
* Checks whether the value is a string.
|
||||
*
|
||||
* @return bool True if the value is a string.
|
||||
*/
|
||||
public function isString(): bool;
|
||||
|
||||
/**
|
||||
* Checks whether the value is an integer.
|
||||
*
|
||||
* @return bool True if the value is an integer.
|
||||
*/
|
||||
public function isInteger(): bool;
|
||||
|
||||
/**
|
||||
* Checks whether the value is a floating point number.
|
||||
*
|
||||
* @return bool True if the value is a floating point number.
|
||||
*/
|
||||
public function isFloat(): bool;
|
||||
|
||||
/**
|
||||
* Checks whether the value is a boolean.
|
||||
*
|
||||
* @return bool True if the value is a boolean.
|
||||
*/
|
||||
public function isBoolean(): bool;
|
||||
|
||||
/**
|
||||
* Checks whether the value is an array.
|
||||
*
|
||||
* @return bool True if the value is an array.
|
||||
*/
|
||||
public function isArray(): bool;
|
||||
|
||||
/**
|
||||
* Gets the raw value without any type validation.
|
||||
*
|
||||
* @return mixed The configuration value.
|
||||
*/
|
||||
public function getValue(): mixed;
|
||||
|
||||
/**
|
||||
* Ensures the value is a string and returns the value.
|
||||
*
|
||||
* @throws UnexpectedValueException If the value is not a string.
|
||||
* @return string String configuration value.
|
||||
*/
|
||||
public function getString(): string;
|
||||
|
||||
/**
|
||||
* Ensures the value is an integer and returns the value.
|
||||
*
|
||||
* @throws UnexpectedValueException If the value is not an integer.
|
||||
* @return int Integer configuration value.
|
||||
*/
|
||||
public function getInteger(): int;
|
||||
|
||||
/**
|
||||
* Ensures the value is a floating point number and returns the value.
|
||||
*
|
||||
* @throws UnexpectedValueException If the value is not a floating point number.
|
||||
* @return float Floating point number configuration value.
|
||||
*/
|
||||
public function getFloat(): float;
|
||||
|
||||
/**
|
||||
* Ensures the value is a boolean and returns the value.
|
||||
*
|
||||
* @throws UnexpectedValueException If the value is not a boolean.
|
||||
* @return bool Boolean configuration value.
|
||||
*/
|
||||
public function getBoolean(): bool;
|
||||
|
||||
/**
|
||||
* Ensures the value is an array and returns the value.
|
||||
*
|
||||
* @throws UnexpectedValueException If the value is not an array.
|
||||
* @return mixed[] Array configuration value.
|
||||
*/
|
||||
public function getArray(): array;
|
||||
}
|
290
src/Config/Db/DbConfig.php
Normal file
290
src/Config/Db/DbConfig.php
Normal file
|
@ -0,0 +1,290 @@
|
|||
<?php
|
||||
// DbConfig.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config\Db;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Index\Config\{Config,GetValueInfoTrait,GetValuesTrait,MutableConfigTrait,ScopedConfig};
|
||||
use Index\Data\{DbConnection,DbStatementCache,DbTools};
|
||||
|
||||
/**
|
||||
* Provides a configuration based on a {@see DbConnection} instance.
|
||||
*
|
||||
* @todo provide table name in constructor
|
||||
* @todo scan for vendor specific queries and generalise them
|
||||
* @todo getValues() parsing should probably be done external so it can be reused
|
||||
*/
|
||||
class DbConfig implements Config {
|
||||
use MutableConfigTrait, GetValueInfoTrait, GetValuesTrait;
|
||||
|
||||
private DbStatementCache $cache;
|
||||
/** @var array<string, DbConfigValueInfo> */
|
||||
private array $values = [];
|
||||
|
||||
/** @var string[] */
|
||||
private array $extraFieldNames;
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $extraFieldValues;
|
||||
|
||||
/**
|
||||
* @param DbConnection $dbConn
|
||||
* @param string $tableName
|
||||
* @param string $nameField
|
||||
* @param string $valueField
|
||||
* @param array<string, mixed> $extraFields
|
||||
*/
|
||||
public function __construct(
|
||||
DbConnection $dbConn,
|
||||
private string $tableName,
|
||||
private string $nameField = 'config_name',
|
||||
private string $valueField = 'config_value',
|
||||
array $extraFields = [],
|
||||
) {
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
$this->extraFieldNames = array_keys($extraFields);
|
||||
$this->extraFieldValues = array_values($extraFields);
|
||||
}
|
||||
|
||||
public static function validateName(string $name): bool {
|
||||
$parts = explode('.', $name);
|
||||
foreach($parts as $part) {
|
||||
if($part === '' || trim($part) !== $part)
|
||||
return false;
|
||||
|
||||
if(preg_match('#^([a-z][a-zA-Z0-9_]+)$#', $part) !== 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets value cache.
|
||||
*/
|
||||
public function reset(): void {
|
||||
$this->values = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads specifics items from the local cache.
|
||||
*
|
||||
* @param string|string[] $names Names of values to unload.
|
||||
*/
|
||||
public function unload(string|array $names): void {
|
||||
if(empty($names))
|
||||
return;
|
||||
if(is_string($names))
|
||||
$names = [$names];
|
||||
|
||||
foreach($names as $name)
|
||||
unset($this->values[$name]);
|
||||
}
|
||||
|
||||
public function getSeparator(): string {
|
||||
return '.';
|
||||
}
|
||||
|
||||
public function scopeTo(string ...$prefix): Config {
|
||||
return new ScopedConfig($this, $prefix);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
$query = sprintf(
|
||||
'SELECT COUNT(*) FROM %s WHERE %s IN (%s)',
|
||||
$this->tableName, $this->nameField,
|
||||
DbTools::prepareListString($nameCount)
|
||||
);
|
||||
foreach($this->extraFieldNames as $extraFieldName)
|
||||
$query .= sprintf(' AND %s = ?', $extraFieldName);
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
|
||||
$args = 0;
|
||||
foreach($names as $name)
|
||||
$stmt->addParameter(++$args, $name);
|
||||
foreach($this->extraFieldValues as $extraFieldValue)
|
||||
$stmt->addParameter(++$args, $extraFieldValue);
|
||||
|
||||
$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);
|
||||
$query = sprintf(
|
||||
'DELETE FROM %s WHERE %s IN (%s)',
|
||||
$this->tableName, $this->nameField,
|
||||
DbTools::prepareListString($nameCount)
|
||||
);
|
||||
foreach($this->extraFieldNames as $extraFieldName)
|
||||
$query .= sprintf(' AND %s = ?', $extraFieldName);
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
|
||||
$args = 0;
|
||||
foreach($names as $name)
|
||||
$stmt->addParameter(++$args, $name);
|
||||
foreach($this->extraFieldValues as $extraFieldValue)
|
||||
$stmt->addParameter(++$args, $extraFieldValue);
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function getAllValueInfos(int $range = 0, int $offset = 0): array {
|
||||
$this->reset();
|
||||
$infos = [];
|
||||
|
||||
$hasRange = $range !== 0;
|
||||
|
||||
$query = sprintf('SELECT %s, %s FROM %s', $this->nameField, $this->valueField, $this->tableName);
|
||||
foreach($this->extraFieldNames as $i => $extraFieldName)
|
||||
$query .= sprintf(' %s %s = ?', $i < 1 ? 'WHERE' : 'AND', $extraFieldName);
|
||||
if($hasRange) {
|
||||
if($range < 0)
|
||||
throw new InvalidArgumentException('$range must be a positive integer');
|
||||
if($offset < 0)
|
||||
throw new InvalidArgumentException('$offset must be greater than zero if a range is specified');
|
||||
|
||||
$query .= ' LIMIT ? OFFSET ?';
|
||||
}
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
|
||||
$args = 0;
|
||||
foreach($this->extraFieldValues as $extraFieldValue)
|
||||
$stmt->addParameter(++$args, $extraFieldValue);
|
||||
if($hasRange) {
|
||||
$stmt->addParameter(++$args, $range);
|
||||
$stmt->addParameter(++$args, $offset);
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
|
||||
while($result->next()) {
|
||||
$name = $result->getString(0);
|
||||
$infos[] = $this->values[$name] = new DbConfigValueInfo($result);
|
||||
}
|
||||
|
||||
return $infos;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if(!empty($names)) {
|
||||
// array_diff preserves keys, the for() later would fuck up without it
|
||||
$names = array_values($names);
|
||||
$nameCount = count($names);
|
||||
|
||||
$query = sprintf(
|
||||
'SELECT %s, %s FROM %s WHERE %s IN (%s)',
|
||||
$this->nameField, $this->valueField, $this->tableName, $this->nameField,
|
||||
DbTools::prepareListString($nameCount)
|
||||
);
|
||||
foreach($this->extraFieldNames as $extraFieldName)
|
||||
$query .= sprintf(' AND %s = ?', $extraFieldName);
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
|
||||
$args = 0;
|
||||
foreach($names as $name)
|
||||
$stmt->addParameter(++$args, $name);
|
||||
foreach($this->extraFieldValues as $extraFieldValue)
|
||||
$stmt->addParameter(++$args, $extraFieldValue);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
while($result->next()) {
|
||||
$name = $result->getString(0);
|
||||
$infos[] = $this->values[$name] = new DbConfigValueInfo($result);
|
||||
}
|
||||
}
|
||||
|
||||
return $infos;
|
||||
}
|
||||
|
||||
public function setValues(array $values): void {
|
||||
if(empty($values))
|
||||
return;
|
||||
|
||||
$fields = [$this->nameField, $this->valueField];
|
||||
if(count($this->extraFieldNames) > 0)
|
||||
$fields = array_merge($fields, $this->extraFieldNames);
|
||||
$fieldCount = count($fields);
|
||||
$fields = implode(', ', $fields);
|
||||
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'INSERT INTO %s (%s) VALUES (%s)',
|
||||
$this->tableName, $fields,
|
||||
DbTools::prepareListString($fieldCount)
|
||||
));
|
||||
|
||||
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');
|
||||
|
||||
$this->removeValues($name);
|
||||
|
||||
$args = 0;
|
||||
$stmt->addParameter(++$args, $name);
|
||||
$stmt->addParameter(++$args, serialize($value));
|
||||
foreach($this->extraFieldValues as $extraFieldValue)
|
||||
$stmt->addParameter(++$args, $extraFieldValue);
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
}
|
94
src/Config/Db/DbConfigValueInfo.php
Normal file
94
src/Config/Db/DbConfigValueInfo.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
// DbConfigValueInfo.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config\Db;
|
||||
|
||||
use UnexpectedValueException;
|
||||
use Index\Config\ConfigValueInfo;
|
||||
use Index\Data\DbResult;
|
||||
|
||||
/**
|
||||
* Provides information about a databased configuration value.
|
||||
*/
|
||||
class DbConfigValueInfo implements ConfigValueInfo {
|
||||
private string $name;
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* @param DbResult $result Database result for this config value.
|
||||
*/
|
||||
public function __construct(DbResult $result) {
|
||||
$this->name = $result->getString(0);
|
||||
$this->value = $result->getString(1);
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return match($this->value[0]) {
|
||||
'b' => 'bool',
|
||||
'a' => 'array',
|
||||
'd' => 'float',
|
||||
'i' => 'int',
|
||||
's' => 'string',
|
||||
default => 'unknown',
|
||||
};
|
||||
}
|
||||
|
||||
public function isBoolean(): bool { return $this->value[0] === 'b'; }
|
||||
public function isArray(): bool { return $this->value[0] === 'a'; }
|
||||
public function isFloat(): bool { return $this->value[0] === 'd'; }
|
||||
public function isInteger(): bool { return $this->value[0] === 'i'; }
|
||||
public function isString(): bool { return $this->value[0] === 's'; }
|
||||
|
||||
public function getValue(): mixed {
|
||||
return unserialize($this->value);
|
||||
}
|
||||
|
||||
public function getString(): string {
|
||||
$value = $this->getValue();
|
||||
if(!is_string($value))
|
||||
throw new UnexpectedValueException('Value is not a string.');
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getInteger(): int {
|
||||
$value = $this->getValue();
|
||||
if(!is_int($value))
|
||||
throw new UnexpectedValueException('Value is not an integer.');
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getFloat(): float {
|
||||
$value = $this->getValue();
|
||||
if(!is_float($value))
|
||||
throw new UnexpectedValueException('Value is not a floating point number.');
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getBoolean(): bool {
|
||||
$value = $this->getValue();
|
||||
if(!is_bool($value))
|
||||
throw new UnexpectedValueException('Value is not a boolean.');
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getArray(): array {
|
||||
$value = $this->getValue();
|
||||
if(!is_array($value))
|
||||
throw new UnexpectedValueException('Value is not an array.');
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function __toString(): string {
|
||||
$value = $this->getValue();
|
||||
if(is_array($value))
|
||||
return implode(', ', $value);
|
||||
|
||||
return (string)$value; // @phpstan-ignore-line dude trust me
|
||||
}
|
||||
}
|
144
src/Config/Fs/FsConfig.php
Normal file
144
src/Config/Fs/FsConfig.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
// FsConfig.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config\Fs;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Index\Config\{Config,GetValueInfoTrait,GetValuesTrait,ImmutableConfigTrait,ScopedConfig};
|
||||
|
||||
/**
|
||||
* Provides a configuration in string based format.
|
||||
*/
|
||||
class FsConfig implements Config {
|
||||
use ImmutableConfigTrait, GetValueInfoTrait, GetValuesTrait;
|
||||
|
||||
/**
|
||||
* @param array<string, FsConfigValueInfo> $values
|
||||
*/
|
||||
public function __construct(private array $values) {}
|
||||
|
||||
public function getSeparator(): string {
|
||||
return ':';
|
||||
}
|
||||
|
||||
public function scopeTo(string ...$prefix): Config {
|
||||
return new ScopedConfig($this, $prefix);
|
||||
}
|
||||
|
||||
public function hasValues(string|array $names): bool {
|
||||
if(is_string($names))
|
||||
return array_key_exists($names, $this->values);
|
||||
|
||||
foreach($names as $name)
|
||||
if(!array_key_exists($name, $this->values))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getAllValueInfos(int $range = 0, int $offset = 0): array {
|
||||
if($range === 0)
|
||||
return array_values($this->values);
|
||||
|
||||
if($range < 0)
|
||||
throw new InvalidArgumentException('$range must be a positive integer');
|
||||
if($offset < 0)
|
||||
throw new InvalidArgumentException('$offset must be greater than zero if a range is specified');
|
||||
|
||||
return array_slice($this->values, $offset, $range);
|
||||
}
|
||||
|
||||
public function getValueInfos(string|array $names): array {
|
||||
if(is_string($names))
|
||||
return array_key_exists($names, $this->values) ? [$this->values[$names]] : [];
|
||||
|
||||
$infos = [];
|
||||
|
||||
foreach($names as $name)
|
||||
if(array_key_exists($name, $this->values))
|
||||
$infos[] = $this->values[$name];
|
||||
|
||||
return $infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of FsConfig from an array of lines.
|
||||
*
|
||||
* @param string[] $lines Config lines.
|
||||
* @return FsConfig
|
||||
*/
|
||||
public static function fromLines(array $lines): self {
|
||||
$values = [];
|
||||
|
||||
foreach($lines as $line) {
|
||||
$line = trim($line);
|
||||
if($line === '' || $line[0] === '#' || $line[0] === ';')
|
||||
continue;
|
||||
|
||||
$info = new FsConfigValueInfo(...explode(' ', $line, 2));
|
||||
$values[$info->getName()] = $info;
|
||||
}
|
||||
|
||||
return new FsConfig($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of FsConfig from a string.
|
||||
*
|
||||
* @param string $lines Config lines.
|
||||
* @param non-empty-string $newLine Line separator character.
|
||||
* @return FsConfig
|
||||
*/
|
||||
public static function fromString(string $lines, string $newLine = "\n"): self {
|
||||
return self::fromLines(explode($newLine, $lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of FsConfig from a file.
|
||||
*
|
||||
* @param string $path Config file path.
|
||||
* @throws InvalidArgumentException If $path does not exist or could not be opened.
|
||||
* @return FsConfig
|
||||
*/
|
||||
public static function fromFile(string $path): self {
|
||||
if(!is_file($path))
|
||||
throw new InvalidArgumentException('$path does not exist');
|
||||
|
||||
$handle = fopen($path, 'rb');
|
||||
if($handle === false)
|
||||
throw new RuntimeException('could not open a file handle from $path');
|
||||
|
||||
try {
|
||||
return self::fromStream($handle);
|
||||
} finally {
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of FsConfig from a readable stream.
|
||||
*
|
||||
* @param resource $stream Config file stream.
|
||||
* @throws InvalidArgumentException If $stream is not a .
|
||||
* @return FsConfig
|
||||
*/
|
||||
public static function fromStream(mixed $stream): self {
|
||||
if(!is_resource($stream))
|
||||
throw new InvalidArgumentException('$stream must be a resource');
|
||||
|
||||
$values = [];
|
||||
while(($line = fgets($stream)) !== false) {
|
||||
$line = trim($line);
|
||||
if($line === '' || $line[0] === '#' || $line[0] === ';')
|
||||
continue;
|
||||
|
||||
$info = new FsConfigValueInfo(...explode(' ', $line, 2));
|
||||
$values[$info->getName()] = $info;
|
||||
}
|
||||
|
||||
return new FsConfig($values);
|
||||
}
|
||||
}
|
83
src/Config/Fs/FsConfigValueInfo.php
Normal file
83
src/Config/Fs/FsConfigValueInfo.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
// FsConfigValueInfo.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config\Fs;
|
||||
|
||||
use Index\Config\ConfigValueInfo;
|
||||
|
||||
/**
|
||||
* Value info for file configs.
|
||||
*/
|
||||
class FsConfigValueInfo implements ConfigValueInfo {
|
||||
private string $name;
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* @param string $name Name of the config value.
|
||||
* @param string $value Value of the config value.
|
||||
*/
|
||||
public function __construct(string $name, string $value = '') {
|
||||
$this->name = $name;
|
||||
$this->value = trim($value);
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
// SharpChat config format is just all strings and casts on demand
|
||||
return 'string';
|
||||
}
|
||||
|
||||
public function isString(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isInteger(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isFloat(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isBoolean(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isArray(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getValue(): mixed {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getString(): string {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getInteger(): int {
|
||||
return (int)$this->value;
|
||||
}
|
||||
|
||||
public function getFloat(): float {
|
||||
return (float)$this->value;
|
||||
}
|
||||
|
||||
public function getBoolean(): bool {
|
||||
return $this->value !== '0'
|
||||
&& strcasecmp($this->value, 'false') !== 0;
|
||||
}
|
||||
|
||||
public function getArray(): array {
|
||||
return explode(' ', $this->value);
|
||||
}
|
||||
|
||||
public function __toString(): string {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
41
src/Config/GetValueInfoTrait.php
Normal file
41
src/Config/GetValueInfoTrait.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
// GetValueInfoTrait.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
/**
|
||||
* Provides implementations for things that are essentially macros for {@see Config::getValueInfos}.
|
||||
*/
|
||||
trait GetValueInfoTrait {
|
||||
public function getValueInfo(string $name): ?ConfigValueInfo {
|
||||
$infos = $this->getValueInfos($name);
|
||||
return empty($infos) ? null : $infos[0];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
97
src/Config/GetValuesTrait.php
Normal file
97
src/Config/GetValuesTrait.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
// GetValuesTrait.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Provides implementation for {@see Config::getValues} based on {@see Config::getValueInfos}.
|
||||
*/
|
||||
trait GetValuesTrait {
|
||||
/**
|
||||
* Format described in {@see Config::getValues}.
|
||||
*
|
||||
* @param array<string|string[]> $specs
|
||||
* @throws InvalidArgumentException If $specs contains an invalid entry.
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getValues(array $specs): array {
|
||||
$names = [];
|
||||
$evald = [];
|
||||
|
||||
foreach($specs as $key => $spec) {
|
||||
if(is_string($spec)) {
|
||||
$name = $spec;
|
||||
$default = null;
|
||||
$alias = null;
|
||||
} elseif(is_array($spec) && !empty($spec)) {
|
||||
$name = $spec[0];
|
||||
$default = $spec[1] ?? null;
|
||||
$alias = $spec[2] ?? null;
|
||||
} else
|
||||
throw new InvalidArgumentException('$specs array contains an invalid entry');
|
||||
|
||||
$nameLength = strlen($name);
|
||||
if($nameLength > 3 && ($colon = strrpos($name, ':')) === $nameLength - 2) {
|
||||
$type = substr($name, $colon + 1, 1);
|
||||
$name = substr($name, 0, $colon);
|
||||
} else $type = '';
|
||||
|
||||
$names[] = $name;
|
||||
$evald[$key] = [
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'default' => $default,
|
||||
'alias' => $alias,
|
||||
];
|
||||
}
|
||||
|
||||
$infos = $this->getValueInfos($names);
|
||||
$results = [];
|
||||
|
||||
foreach($evald as $spec) {
|
||||
foreach($infos as $infoTest)
|
||||
if($infoTest->getName() === $spec['name']) {
|
||||
$info = $infoTest;
|
||||
break;
|
||||
}
|
||||
|
||||
$resultName = $spec['alias'] ?? $spec['name'];
|
||||
|
||||
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 InvalidArgumentException(sprintf('invalid type letter encountered: "%s"', $spec['type'])),
|
||||
});
|
||||
|
||||
$results[$resultName] = $defaultValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$resultName] = 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 InvalidArgumentException('unknown type encountered in $specs'),
|
||||
};
|
||||
|
||||
unset($info);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
41
src/Config/ImmutableConfigTrait.php
Normal file
41
src/Config/ImmutableConfigTrait.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
// ImmutableConfigTrait.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Intercepts mutable methods required to be implemented by {@see Config} and returns exceptions.
|
||||
*/
|
||||
trait ImmutableConfigTrait {
|
||||
public function removeValues(string|array $names): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
|
||||
public function setValues(array $values): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
|
||||
public function setString(string $name, string $value): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
|
||||
public function setInteger(string $name, int $value): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
|
||||
public function setFloat(string $name, float $value): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
|
||||
public function setBoolean(string $name, bool $value): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
|
||||
public function setArray(string $name, array $value): void {
|
||||
throw new RuntimeException('this configuration is read only');
|
||||
}
|
||||
}
|
31
src/Config/MutableConfigTrait.php
Normal file
31
src/Config/MutableConfigTrait.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
// MutableConfigTrait.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
/**
|
||||
* Defines set aliases so you don't have to.
|
||||
*/
|
||||
trait MutableConfigTrait {
|
||||
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]);
|
||||
}
|
||||
}
|
69
src/Config/Null/NullConfig.php
Normal file
69
src/Config/Null/NullConfig.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
// NullConfig.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config\Null;
|
||||
|
||||
use Index\Config\{Config,ConfigValueInfo,GetValuesTrait};
|
||||
|
||||
/**
|
||||
* Provides a black hole configuration that will always return the default values.
|
||||
*/
|
||||
class NullConfig implements Config {
|
||||
use GetValuesTrait;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function getSeparator(): string {
|
||||
return "\0";
|
||||
}
|
||||
|
||||
public function scopeTo(string ...$prefix): Config {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasValues(string|array $names): bool {
|
||||
return is_array($names) && empty($names);
|
||||
}
|
||||
|
||||
public function getAllValueInfos(int $range = 0, int $offset = 0): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getValueInfos(string|array $names): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getValueInfo(string $name): ?ConfigValueInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getString(string $name, string $default = ''): string {
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getInteger(string $name, int $default = 0): int {
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getFloat(string $name, float $default = 0): float {
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getBoolean(string $name, bool $default = false): bool {
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getArray(string $name, array $default = []): array {
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function removeValues(string|array $names): void {}
|
||||
public function setValues(array $values): void {}
|
||||
public function setString(string $name, string $value): void {}
|
||||
public function setInteger(string $name, int $value): void {}
|
||||
public function setFloat(string $name, float $value): void {}
|
||||
public function setBoolean(string $name, bool $value): void {}
|
||||
public function setArray(string $name, array $value): void {}
|
||||
}
|
156
src/Config/ScopedConfig.php
Normal file
156
src/Config/ScopedConfig.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
// ScopedConfig.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Provides a scoped configuration instead.
|
||||
*/
|
||||
class ScopedConfig implements Config {
|
||||
private Config $config;
|
||||
private string $prefix;
|
||||
/** @var non-empty-array<string> */
|
||||
private array $prefixRaw;
|
||||
private int $prefixLength;
|
||||
|
||||
/** @param string[] $prefixRaw */
|
||||
public function __construct(Config $config, array $prefixRaw) {
|
||||
if(empty($prefixRaw))
|
||||
throw new InvalidArgumentException('$prefix may not be empty');
|
||||
|
||||
$scopeChar = $config->getSeparator();
|
||||
$prefix = implode($scopeChar, $prefixRaw) . $scopeChar;
|
||||
|
||||
$this->config = $config;
|
||||
$this->prefix = $prefix;
|
||||
$this->prefixRaw = $prefixRaw;
|
||||
$this->prefixLength = strlen($prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|string[] $names
|
||||
* @return string[]
|
||||
*/
|
||||
private function prefixNames(string|array $names): array {
|
||||
if(is_string($names))
|
||||
return [$this->prefix . $names];
|
||||
|
||||
foreach($names as $key => $name)
|
||||
$names[$key] = $this->prefix . $name;
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
private function prefixName(string $name): string {
|
||||
return $this->prefix . $name;
|
||||
}
|
||||
|
||||
public function scopeTo(string ...$prefix): Config {
|
||||
return $this->config->scopeTo(...array_merge($this->prefixRaw, $prefix));
|
||||
}
|
||||
|
||||
public function getSeparator(): string {
|
||||
return $this->config->getSeparator();
|
||||
}
|
||||
|
||||
public function hasValues(string|array $names): bool {
|
||||
return $this->config->hasValues($this->prefixNames($names));
|
||||
}
|
||||
|
||||
public function removeValues(string|array $names): void {
|
||||
$this->config->removeValues($this->prefixNames($names));
|
||||
}
|
||||
|
||||
public function getAllValueInfos(int $range = 0, int $offset = 0): array {
|
||||
$infos = $this->config->getAllValueInfos($range, $offset);
|
||||
foreach($infos as $key => $info)
|
||||
$infos[$key] = new ScopedConfigValueInfo($info, $this->prefixLength);
|
||||
return $infos;
|
||||
}
|
||||
|
||||
public function getValueInfos(string|array $names): array {
|
||||
$infos = $this->config->getValueInfos($this->prefixNames($names));
|
||||
foreach($infos as $key => $info)
|
||||
$infos[$key] = new ScopedConfigValueInfo($info, $this->prefixLength);
|
||||
return $infos;
|
||||
}
|
||||
|
||||
public function getValueInfo(string $name): ?ConfigValueInfo {
|
||||
$info = $this->config->getValueInfo($this->prefixName($name));
|
||||
if($info !== null)
|
||||
$info = new ScopedConfigValueInfo($info, $this->prefixLength);
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function getValues(array $specs): array {
|
||||
foreach($specs as $key => $spec) {
|
||||
if(is_string($spec))
|
||||
$specs[$key] = $this->prefixName($spec);
|
||||
elseif(is_array($spec) && !empty($spec))
|
||||
$specs[$key][0] = $this->prefixName($spec[0]);
|
||||
else
|
||||
throw new InvalidArgumentException('$specs array contains an invalid entry');
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach($this->config->getValues($specs) as $name => $result)
|
||||
// prefix removal should probably be done with a whitelist of sorts
|
||||
$results[str_starts_with($name, $this->prefix) ? substr($name, $this->prefixLength) : $name] = $result;
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getString(string $name, string $default = ''): string {
|
||||
return $this->config->getString($this->prefixName($name), $default);
|
||||
}
|
||||
|
||||
public function getInteger(string $name, int $default = 0): int {
|
||||
return $this->config->getInteger($this->prefixName($name), $default);
|
||||
}
|
||||
|
||||
public function getFloat(string $name, float $default = 0): float {
|
||||
return $this->config->getFloat($this->prefixName($name), $default);
|
||||
}
|
||||
|
||||
public function getBoolean(string $name, bool $default = false): bool {
|
||||
return $this->config->getBoolean($this->prefixName($name), $default);
|
||||
}
|
||||
|
||||
public function getArray(string $name, array $default = []): array {
|
||||
return $this->config->getArray($this->prefixName($name), $default);
|
||||
}
|
||||
|
||||
public function setValues(array $values): void {
|
||||
if(empty($values))
|
||||
return;
|
||||
|
||||
$prefixed = [];
|
||||
foreach($values as $name => $value)
|
||||
$prefixed[$this->prefixName($name)] = $value;
|
||||
$this->config->setValues($values);
|
||||
}
|
||||
|
||||
public function setString(string $name, string $value): void {
|
||||
$this->config->setString($this->prefixName($name), $value);
|
||||
}
|
||||
|
||||
public function setInteger(string $name, int $value): void {
|
||||
$this->config->setInteger($this->prefixName($name), $value);
|
||||
}
|
||||
|
||||
public function setFloat(string $name, float $value): void {
|
||||
$this->config->setFloat($this->prefixName($name), $value);
|
||||
}
|
||||
|
||||
public function setBoolean(string $name, bool $value): void {
|
||||
$this->config->setBoolean($this->prefixName($name), $value);
|
||||
}
|
||||
|
||||
public function setArray(string $name, array $value): void {
|
||||
$this->config->setArray($this->prefixName($name), $value);
|
||||
}
|
||||
}
|
85
src/Config/ScopedConfigValueInfo.php
Normal file
85
src/Config/ScopedConfigValueInfo.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
// ScopedConfigValueInfo.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
namespace Index\Config;
|
||||
|
||||
/**
|
||||
* Provides information about a scoped configuration value.
|
||||
*/
|
||||
class ScopedConfigValueInfo implements ConfigValueInfo {
|
||||
/**
|
||||
* @param ConfigValueInfo $info Base config value info instance.
|
||||
* @param int $prefixLength Length of the prefix.
|
||||
*/
|
||||
public function __construct(
|
||||
private ConfigValueInfo $info,
|
||||
private int $prefixLength
|
||||
) {}
|
||||
|
||||
public function getName(): string {
|
||||
return substr($this->info->getName(), $this->prefixLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real name of the configuration value, without removing the prefix.
|
||||
*
|
||||
* @return string Unprefixed configuration value.
|
||||
*/
|
||||
public function getRealName(): string {
|
||||
return $this->info->getName();
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return $this->info->getType();
|
||||
}
|
||||
|
||||
public function isString(): bool {
|
||||
return $this->info->isString();
|
||||
}
|
||||
|
||||
public function isInteger(): bool {
|
||||
return $this->info->isInteger();
|
||||
}
|
||||
|
||||
public function isFloat(): bool {
|
||||
return $this->info->isFloat();
|
||||
}
|
||||
|
||||
public function isBoolean(): bool {
|
||||
return $this->info->isBoolean();
|
||||
}
|
||||
|
||||
public function isArray(): bool {
|
||||
return $this->info->isArray();
|
||||
}
|
||||
|
||||
public function getValue(): mixed {
|
||||
return $this->info->getValue();
|
||||
}
|
||||
|
||||
public function getString(): string {
|
||||
return $this->info->getString();
|
||||
}
|
||||
|
||||
public function getInteger(): int {
|
||||
return $this->info->getInteger();
|
||||
}
|
||||
|
||||
public function getFloat(): float {
|
||||
return $this->info->getFloat();
|
||||
}
|
||||
|
||||
public function getBoolean(): bool {
|
||||
return $this->info->getBoolean();
|
||||
}
|
||||
|
||||
public function getArray(): array {
|
||||
return $this->info->getArray();
|
||||
}
|
||||
|
||||
public function __toString(): string {
|
||||
return (string)$this->info;
|
||||
}
|
||||
}
|
182
tests/DbConfigTest.php
Normal file
182
tests/DbConfigTest.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
// DbConfigTest.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Index\Config\{GetValueInfoTrait,GetValuesTrait,MutableConfigTrait};
|
||||
use Index\Config\Db\{DbConfig,DbConfigValueInfo};
|
||||
use Index\Data\{DbConnection,DbTools};
|
||||
|
||||
#[CoversClass(DbConfig::class)]
|
||||
#[CoversClass(DbConfigValueInfo::class)]
|
||||
#[CoversClass(MutableConfigTrait::class)]
|
||||
#[CoversClass(GetValueInfoTrait::class)]
|
||||
#[CoversClass(GetValuesTrait::class)]
|
||||
final class DbConfigTest extends TestCase {
|
||||
private DbConnection $dbConn;
|
||||
private DbConfig $config;
|
||||
|
||||
private const VALUES = [
|
||||
'private.allow_password_reset' => 'b:1;',
|
||||
'private.enable' => 'b:0;',
|
||||
'private.msg' => 's:71:"Things are happening. Check back later for something new... eventually.";',
|
||||
'private.perm.cat' => 's:4:"user";',
|
||||
'private.perm.val' => 'i:1;',
|
||||
'site.desc' => 's:38:"The internet\'s last convenience store.";',
|
||||
'site.ext_logo' => 's:51:"https://static.flash.moe/images/flashii-logo-v3.png";',
|
||||
'site.name' => 's:5:"Edgii";',
|
||||
'site.social.bsky' => 's:36:"https://bsky.app/profile/flashii.net";',
|
||||
'site.url' => 's:18:"https://edgii.net/";',
|
||||
'test.array' => 'a:5:{i:0;i:1234;i:1;d:56.789;i:2;s:6:"Mewow!";i:3;b:1;i:4;s:4:"jeff";}',
|
||||
'test.bool' => 'b:1;',
|
||||
'test.float' => 'd:9876.4321;',
|
||||
'test.int' => 'i:243230;',
|
||||
];
|
||||
|
||||
private const USER_VALUES = [
|
||||
'mobile.left_handed' => ['b:0;', 'b:1;'],
|
||||
'profile.allow_indexing' => ['b:1;', 'b:0;'],
|
||||
];
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->dbConn = DbTools::create('sqlite::memory:');
|
||||
$this->dbConn->execute('CREATE TABLE skh_config (config_name TEXT NOT NULL COLLATE NOCASE, config_value BLOB NOT NULL, PRIMARY KEY (config_name))');
|
||||
$this->dbConn->execute('CREATE TABLE skh_user_settings (user_id INTEGER NOT NULL, setting_name TEXT NOT NULL COLLATE NOCASE, setting_value BLOB NOT NULL, PRIMARY KEY (user_id, setting_name))');
|
||||
|
||||
$stmt = $this->dbConn->prepare('INSERT INTO skh_config (config_name, config_value) VALUES (?, ?)');
|
||||
foreach(self::VALUES as $name => $value) {
|
||||
$stmt->addParameter(1, $name);
|
||||
$stmt->addParameter(2, $value);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$stmt = $this->dbConn->prepare('INSERT INTO skh_user_settings (user_id, setting_name, setting_value) VALUES (?, ?, ?)');
|
||||
for($i = 1; $i <= 10; ++$i)
|
||||
foreach(self::USER_VALUES as $name => $value) {
|
||||
$stmt->addParameter(1, $i);
|
||||
$stmt->addParameter(2, $name);
|
||||
$stmt->addParameter(3, $value[$i % 2]);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$this->config = new DbConfig($this->dbConn, 'skh_config');
|
||||
}
|
||||
|
||||
public function testScoping(): void {
|
||||
$this->assertEquals('user', $this->config->getString('private.perm.cat'));
|
||||
$this->assertEquals('Edgii', $this->config->getString('site.name'));
|
||||
|
||||
$scoped = $this->config->scopeTo('private', 'perm');
|
||||
$this->assertEquals('user', $scoped->getString('cat'));
|
||||
}
|
||||
|
||||
public function testHasValues(): void {
|
||||
// hasValues should always return true when the list is empty
|
||||
$this->assertTrue($this->config->hasValues([]));
|
||||
$this->assertFalse($this->config->hasValues('meow'));
|
||||
$this->assertTrue($this->config->hasValues('site.desc'));
|
||||
$this->assertTrue($this->config->hasValues(['site.ext_logo', 'site.url']));
|
||||
$this->assertFalse($this->config->hasValues(['site.ext_logo', 'site.url', 'site.gun']));
|
||||
}
|
||||
|
||||
public function testGetAllValueInfos(): void {
|
||||
$all = $this->config->getAllValueInfos();
|
||||
$expected = array_keys(self::VALUES);
|
||||
$values = [];
|
||||
|
||||
foreach($all as $info)
|
||||
$values[] = $info->getName();
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
|
||||
$subset = $this->config->getAllValueInfos(2, 3);
|
||||
$expected = [
|
||||
'private.perm.cat',
|
||||
'private.perm.val',
|
||||
];
|
||||
$values = [];
|
||||
|
||||
foreach($subset as $info)
|
||||
$values[] = $info->getName();
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
}
|
||||
|
||||
public function testGetValues(): void {
|
||||
$this->assertNull($this->config->getValueInfo('doesnotexist'));
|
||||
|
||||
$scoped = $this->config->scopeTo('private', 'perm');
|
||||
|
||||
$expected = ['private.perm.cat' => 'user', 'private.perm.val' => 1];
|
||||
$values = [];
|
||||
$valueInfos = $scoped->getValueInfos(['cat', 'val', 'poop']);
|
||||
foreach($valueInfos as $valueInfo)
|
||||
$values[$valueInfo->getRealName()] = $valueInfo->getValue();
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
|
||||
$scoped = $this->config->scopeTo('site')->scopeTo('social');
|
||||
|
||||
$expected = [
|
||||
'bsky' => 'https://bsky.app/profile/flashii.net',
|
||||
'bsky_show' => true,
|
||||
'twitter' => '',
|
||||
'twitterShow' => false,
|
||||
];
|
||||
$values = $scoped->getValues([
|
||||
'bsky',
|
||||
['bsky_show:b', true],
|
||||
'twitter:s',
|
||||
['twitter_show:b', false, 'twitterShow'],
|
||||
]);
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
|
||||
$this->assertEquals('', $this->config->getString('none.string'));
|
||||
$this->assertEquals('test', $this->config->getString('none.string', 'test'));
|
||||
$this->assertEquals('https://edgii.net/', $this->config->getString('site.url'));
|
||||
$this->assertEquals(0, $this->config->getInteger('site.url'));
|
||||
|
||||
$this->assertEquals(0, $this->config->getInteger('none.int'));
|
||||
$this->assertEquals(10, $this->config->getInteger('none.int', 10));
|
||||
$this->assertEquals(243230, $this->config->getInteger('test.int'));
|
||||
$this->assertEquals('', $this->config->getString('test.int'));
|
||||
|
||||
$this->assertEquals(0, $this->config->getFloat('none.float'));
|
||||
$this->assertEquals(0.1, $this->config->getFloat('none.float', 0.1));
|
||||
$this->assertEquals(9876.4321, $this->config->getFloat('test.float'));
|
||||
$this->assertEmpty($this->config->getArray('test.float'));
|
||||
|
||||
$this->assertEquals(false, $this->config->getBoolean('none.bool'));
|
||||
$this->assertEquals(true, $this->config->getBoolean('none.bool', true));
|
||||
$this->assertEquals(true, $this->config->getBoolean('test.bool'));
|
||||
$this->assertEquals(false, $this->config->getBoolean('private.msg'));
|
||||
$this->assertEquals(0, $this->config->getFloat('test.bool'));
|
||||
|
||||
$this->assertEmpty($this->config->getArray('none.array'));
|
||||
$this->assertEquals(['de', 'het', 'een'], $this->config->getArray('none.array', ['de', 'het', 'een']));
|
||||
$this->assertEquals([1234, 56.789, 'Mewow!', true, 'jeff'], $this->config->getArray('test.array'));
|
||||
$this->assertEquals(false, $this->config->getBoolean('test.array'));
|
||||
}
|
||||
|
||||
public function testNameValidation(): void {
|
||||
$this->assertTrue(DbConfig::validateName('th1s.iS.vAL1d'));
|
||||
$this->assertFalse(DbConfig::validateName(''));
|
||||
$this->assertFalse(DbConfig::validateName('this..is.not.valid'));
|
||||
$this->assertFalse(DbConfig::validateName('this..is.not.valid'));
|
||||
$this->assertFalse(DbConfig::validateName('First.may.Not.be.uppercase'));
|
||||
}
|
||||
|
||||
public function testUserSettings(): void {
|
||||
for($i = 1; $i <= 10; ++$i) {
|
||||
$config = new DbConfig($this->dbConn, 'skh_user_settings', 'setting_name', 'setting_value', ['user_id' => $i]);
|
||||
|
||||
foreach(self::USER_VALUES as $name => $value)
|
||||
$this->assertEquals(unserialize($value[$i % 2]), $config->getBoolean($name));
|
||||
}
|
||||
}
|
||||
}
|
191
tests/FsConfigTest.php
Normal file
191
tests/FsConfigTest.php
Normal file
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
// FsConfigTest.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Index\Config\{GetValueInfoTrait,GetValuesTrait,ImmutableConfigTrait};
|
||||
use Index\Config\Fs\{FsConfig,FsConfigValueInfo};
|
||||
|
||||
#[CoversClass(FsConfig::class)]
|
||||
#[CoversClass(FsConfigValueInfo::class)]
|
||||
#[CoversClass(ImmutableConfigTrait::class)]
|
||||
#[CoversClass(GetValueInfoTrait::class)]
|
||||
#[CoversClass(GetValuesTrait::class)]
|
||||
final class FsConfigTest extends TestCase {
|
||||
public function testImmutableRemove(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('test value')->removeValues('test');
|
||||
}
|
||||
|
||||
public function testImmutableSetString(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('test value')->setString('test', 'the');
|
||||
}
|
||||
|
||||
public function testImmutableSetInteger(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('test 1234')->setInteger('test', 5678);
|
||||
}
|
||||
|
||||
public function testImmutableSetFloat(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('test 56.78')->setFloat('test', 12.34);
|
||||
}
|
||||
|
||||
public function testImmutableSetBoolean(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('test true')->setBoolean('test', false);
|
||||
}
|
||||
|
||||
public function testImmutableSetArray(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('test words words words')->setArray('test', ['meow', 'meow', 'meow']);
|
||||
}
|
||||
|
||||
public function testImmutableSetValues(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
FsConfig::fromString('')->setValues([
|
||||
'stringval' => 'the',
|
||||
'intval' => 1234,
|
||||
'floatval' => 56.78,
|
||||
'boolval' => true,
|
||||
'arrval' => ['meow'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testScoping(): void {
|
||||
$config = FsConfig::fromLines([
|
||||
'test Inaccessible',
|
||||
'scoped:test Accessible',
|
||||
]);
|
||||
|
||||
$this->assertEquals('Inaccessible', $config->getString('test'));
|
||||
$this->assertEquals('Accessible', $config->getString('scoped:test'));
|
||||
|
||||
$scoped = $config->scopeTo('scoped');
|
||||
$this->assertEquals('Accessible', $scoped->getString('test'));
|
||||
}
|
||||
|
||||
public function testHasValues(): void {
|
||||
$config = FsConfig::fromLines([
|
||||
'test 123',
|
||||
'scoped:test true',
|
||||
'scoped:meow meow',
|
||||
]);
|
||||
|
||||
// hasValues should always return true when the list is empty
|
||||
$this->assertTrue($config->hasValues([]));
|
||||
$this->assertFalse($config->hasValues('meow'));
|
||||
$this->assertTrue($config->hasValues('test'));
|
||||
$this->assertFalse($config->hasValues(['test', 'meow']));
|
||||
$this->assertTrue($config->hasValues(['scoped:test', 'scoped:meow']));
|
||||
}
|
||||
|
||||
public function testGetAllValueInfos(): void {
|
||||
$config = FsConfig::fromFile(__DIR__ . '/sharpchat.cfg');
|
||||
|
||||
$all = $config->getAllValueInfos();
|
||||
$expected = [
|
||||
'chat:port',
|
||||
'chat:msgMaxLength',
|
||||
'chat:floodKickLength',
|
||||
'chat:channels',
|
||||
'chat:channels:lounge:name',
|
||||
'chat:channels:lounge:autoJoin',
|
||||
'chat:channels:prog:name',
|
||||
'chat:channels:games:name',
|
||||
'chat:channels:splat:name',
|
||||
'chat:channels:passwd:name',
|
||||
'chat:channels:passwd:password',
|
||||
'chat:channels:staff:name',
|
||||
'chat:channels:staff:minRank',
|
||||
'msz:secret',
|
||||
'msz:url',
|
||||
'mariadb:host',
|
||||
'mariadb:user',
|
||||
'mariadb:pass',
|
||||
'mariadb:db',
|
||||
];
|
||||
$values = [];
|
||||
|
||||
foreach($all as $info)
|
||||
$values[] = $info->getName();
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
|
||||
$subset = $config->getAllValueInfos(3, 6);
|
||||
$expected = [
|
||||
'chat:channels:prog:name',
|
||||
'chat:channels:games:name',
|
||||
'chat:channels:splat:name',
|
||||
];
|
||||
$values = [];
|
||||
|
||||
foreach($subset as $info)
|
||||
$values[] = $info->getName();
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
}
|
||||
|
||||
public function testGetValues(): void {
|
||||
$config = FsConfig::fromFile(__DIR__ . '/sharpchat.cfg');
|
||||
|
||||
$this->assertNull($config->getValueInfo('doesnotexist'));
|
||||
|
||||
$scoped = $config->scopeTo('chat')->scopeTo('channels', 'passwd');
|
||||
|
||||
$expected = ['chat:channels:passwd:name' => 'Password', 'chat:channels:passwd:password' => 'meow'];
|
||||
$values = [];
|
||||
$valueInfos = $scoped->getValueInfos(['name', 'password', 'minRank']);
|
||||
foreach($valueInfos as $valueInfo)
|
||||
$values[$valueInfo->getRealName()] = $valueInfo->getValue();
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
|
||||
$scoped = $config->scopeTo('chat', 'channels', 'lounge');
|
||||
|
||||
$expected = [
|
||||
'name' => 'Lounge',
|
||||
'auto_join' => true,
|
||||
'minRank' => 0,
|
||||
];
|
||||
$values = $scoped->getValues([
|
||||
'name',
|
||||
['autoJoin:b', false, 'auto_join'],
|
||||
'minRank:i',
|
||||
]);
|
||||
|
||||
$this->assertEquals($expected, $values);
|
||||
|
||||
$this->assertEquals('', $config->getString('msz:url2'));
|
||||
$this->assertEquals('test', $config->getString('msz:url2', 'test'));
|
||||
$this->assertEquals('https://flashii.net/_sockchat', $config->getString('msz:url'));
|
||||
|
||||
$this->assertEquals(0, $config->getInteger('chat:connMaxCount'));
|
||||
$this->assertEquals(10, $config->getInteger('chat:connMaxCount', 10));
|
||||
$this->assertEquals(30, $config->getInteger('chat:floodKickLength'));
|
||||
$this->assertEquals('30', $config->getString('chat:floodKickLength'));
|
||||
|
||||
$this->assertEquals(0, $config->getFloat('boat'));
|
||||
$this->assertEquals(0.1, $config->getFloat('boat', 0.1));
|
||||
$this->assertEquals(192.168, $config->getFloat('mariadb:host'));
|
||||
$this->assertEquals('192.168.0.123', $config->getString('mariadb:host'));
|
||||
|
||||
$this->assertEquals(false, $config->getBoolean('nonexist'));
|
||||
$this->assertEquals(true, $config->getBoolean('nonexist', true));
|
||||
$this->assertEquals(true, $config->getBoolean('chat:channels:lounge:autoJoin'));
|
||||
$this->assertEquals('true', $config->getString('chat:channels:lounge:autoJoin'));
|
||||
$this->assertEquals(true, $config->getBoolean('mariadb:db'));
|
||||
|
||||
$this->assertEmpty($config->getArray('nonexist'));
|
||||
$this->assertEquals(['de', 'het', 'een'], $config->getArray('nonexist', ['de', 'het', 'een']));
|
||||
$this->assertEquals(['lounge', 'prog', 'games', 'splat', 'passwd', 'staff'], $config->getArray('chat:channels'));
|
||||
$this->assertEquals('lounge prog games splat passwd staff', $config->getString('chat:channels'));
|
||||
$this->assertEquals(['fake', 'secret', 'meow'], $config->getArray('msz:secret'));
|
||||
$this->assertEquals('fake secret meow', $config->getString('msz:secret'));
|
||||
}
|
||||
}
|
81
tests/NullConfigTest.php
Normal file
81
tests/NullConfigTest.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
// NullConfigTest.php
|
||||
// Created: 2023-10-20
|
||||
// Updated: 2024-10-04
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Index\Config\GetValuesTrait;
|
||||
use Index\Config\Null\NullConfig;
|
||||
|
||||
#[CoversClass(NullConfig::class)]
|
||||
#[CoversClass(GetValuesTrait::class)]
|
||||
final class NullConfigTest extends TestCase {
|
||||
public function testNullConfig(): void {
|
||||
$config = new NullConfig;
|
||||
|
||||
// no-ops but run anyway to ensure no screaming
|
||||
$config->removeValues('test');
|
||||
$config->setString('stringval', 'the');
|
||||
$config->setInteger('intval', 1234);
|
||||
$config->setFloat('floatval', 56.78);
|
||||
$config->setBoolean('boolval', true);
|
||||
$config->setArray('arrval', ['meow']);
|
||||
$config->setValues([
|
||||
'stringval' => 'the',
|
||||
'intval' => 1234,
|
||||
'floatval' => 56.78,
|
||||
'boolval' => true,
|
||||
'arrval' => ['meow'],
|
||||
]);
|
||||
|
||||
// NullConfig currently returns itself when scoping
|
||||
// might change this depending on whether the scope prefix will be exposed or not
|
||||
$scoped = $config->scopeTo('scoped');
|
||||
$this->assertEquals($config, $scoped);
|
||||
|
||||
// hasValues should always return true when the list is empty
|
||||
$this->assertTrue($config->hasValues([]));
|
||||
$this->assertFalse($config->hasValues('anything'));
|
||||
$this->assertFalse($config->hasValues(['manything1', 'manything2']));
|
||||
|
||||
$this->assertEmpty($config->getAllValueInfos());
|
||||
$this->assertEmpty($config->getValueInfos('the'));
|
||||
|
||||
$expected = [
|
||||
'test_no_type' => null,
|
||||
'test_yes_type' => false,
|
||||
'test_no_type_yes_default' => 1234,
|
||||
'test_yes_type_yes_default' => 56.78,
|
||||
'aliased' => null,
|
||||
];
|
||||
$values = $config->getValues([
|
||||
'test_no_type',
|
||||
'test_yes_type:b',
|
||||
['test_no_type_yes_default', 1234],
|
||||
['test_yes_type_yes_default:d', 56.78],
|
||||
['test_no_default_yes_alias', null, 'aliased'],
|
||||
]);
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected, $values);
|
||||
|
||||
$this->assertNull($config->getValueInfo('value'));
|
||||
|
||||
$this->assertEquals('', $config->getString('string'));
|
||||
$this->assertEquals('default', $config->getString('string', 'default'));
|
||||
|
||||
$this->assertEquals(0, $config->getInteger('int'));
|
||||
$this->assertEquals(960, $config->getInteger('int', 960));
|
||||
|
||||
$this->assertEquals(0, $config->getFloat('float'));
|
||||
$this->assertEquals(67.7, $config->getFloat('float', 67.7));
|
||||
|
||||
$this->assertFalse($config->getBoolean('bool'));
|
||||
$this->assertTrue($config->getBoolean('bool', true));
|
||||
|
||||
$this->assertEmpty($config->getArray('arr'));
|
||||
$this->assertEqualsCanonicalizing(['de', 'het', 'een'], $config->getArray('the', ['de', 'het', 'een']));
|
||||
}
|
||||
}
|
36
tests/sharpchat.cfg
Normal file
36
tests/sharpchat.cfg
Normal file
|
@ -0,0 +1,36 @@
|
|||
# and ; can be used at the start of a line for comments.
|
||||
|
||||
# General Configuration
|
||||
chat:port 6770
|
||||
chat:msgMaxLength 5000
|
||||
;chat:connMaxCount 5
|
||||
chat:floodKickLength 30
|
||||
|
||||
# Channels
|
||||
chat:channels lounge prog games splat passwd staff
|
||||
|
||||
# Lounge channel settings
|
||||
chat:channels:lounge:name Lounge
|
||||
chat:channels:lounge:autoJoin true
|
||||
|
||||
chat:channels:prog:name Programming
|
||||
chat:channels:games:name Games
|
||||
chat:channels:splat:name Splatoon
|
||||
|
||||
# Passworded channel
|
||||
chat:channels:passwd:name Password
|
||||
chat:channels:passwd:password meow
|
||||
|
||||
# Staff channel settings
|
||||
chat:channels:staff:name Staff
|
||||
chat:channels:staff:minRank 5
|
||||
|
||||
# Misuzu integration settings
|
||||
msz:secret fake secret meow
|
||||
msz:url https://flashii.net/_sockchat
|
||||
|
||||
# MariaDB configuration
|
||||
mariadb:host 192.168.0.123
|
||||
mariadb:user chat
|
||||
mariadb:pass nyaa
|
||||
mariadb:db chat
|
Loading…
Reference in a new issue