144 lines
4.1 KiB
PHP
144 lines
4.1 KiB
PHP
<?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);
|
|
}
|
|
}
|