Initialize Nest configuration library.

This commit is contained in:
Madeorsk 2024-11-08 12:45:25 +01:00
commit a04d49f273
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
7 changed files with 313 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
# IDEA
.idea/
*.iml
# Composer
vendor/

27
composer.json Normal file
View file

@ -0,0 +1,27 @@
{
"version": "1.0",
"name": "nest/configuration",
"description": "Nest configuration service.",
"type": "library",
"authors": [
{
"name": "Madeorsk",
"email": "madeorsk@protonmail.com"
}
],
"autoload": {
"psr-4": {
"Nest\\Configuration\\": "src/"
}
},
"repositories": [
{
"type": "vcs",
"url": "https://code.zeptotech.net/Nest/Core"
}
],
"require": {
"php": "^8.3",
"nest/core": "dev-main"
}
}

56
composer.lock generated Normal file
View file

@ -0,0 +1,56 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "456694be683ba1dd09042acde50b7e2a",
"packages": [
{
"name": "nest/core",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://code.zeptotech.net/Nest/Core",
"reference": "22296b8d7c8b528089189a8d9500395424cb6468"
},
"require": {
"php": "^8.3"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"Nest\\": "src/"
},
"files": [
"src/Utils/Array.php",
"src/Utils/Paths.php",
"src/Utils/Reflection.php",
"src/Utils/String.php"
]
},
"authors": [
{
"name": "Madeorsk",
"email": "madeorsk@protonmail.com"
}
],
"description": "Nest framework core.",
"time": "2024-11-08T11:12:38+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"nest/core": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^8.3"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

164
src/Configuration.php Normal file
View file

@ -0,0 +1,164 @@
<?php
namespace Nest\Configuration;
use Nest\Application;
use Nest\Configuration\Exceptions\ConfigurationValueNotFoundException;
use function Nest\Utils\path_join;
/**
* Nest configuration manager.
*/
class Configuration
{
/**
* Loaded files array.
* @var string[]
*/
protected array $loadedFiles;
/**
* The configuration main associative array.
* @var array<string, mixed>
*/
protected array $config;
/**
* Create a new configuration manager for the given application.
* @param Application $application The application.
*/
public function __construct(protected Application $application)
{
$this->reload();
}
/**
* Scan a configuration directory.
* @param string $path The configuration directory path.
* @param string|null $globalContextIdentifier The global context identifier to look for, if there is one.
* @return void
*/
private function scanDirectory(string $path, ?string $globalContextIdentifier = null): void
{
// Get all files in the config path.
$files = scandir($path);
foreach ($files as $file)
{
if (is_file(path_join($path, $file)) && str_ends_with($file, ".php"))
// If it looks like a configuration file, we try to load it.
$this->loadedFiles[] = path_join($path, $file);
if (!empty($globalContextIdentifier) && is_dir(path_join($path, $file)) && $globalContextIdentifier == $file)
{ // If it looks like the global context custom configuration directory: we try to load all configuration files in it.
$this->scanDirectory(path_join($path, $file));
}
}
}
/**
* Reload all configuration files in the configuration path.
* @return void
*/
public function reload(): void
{
// Initialize loaded files array.
$this->loadedFiles = [];
// Scan the main configuration directory, searching for config files or global context custom config directory.
$this->scanDirectory($this->application->paths()->getConfigPath(), $this->application->getGlobalContextIdentifier());
sort($this->loadedFiles); // Sort files alphabetically.
// Reset configuration.
$this->config = [];
// Read all sorted configuration files.
foreach ($this->loadedFiles as $configFile)
$this->readConfigFile($configFile);
}
/**
* Read a configuration file and merge it with the current configuration state.
* @param string $filePath The configuration file path.
* @return $this
*/
public function readConfigFile(string $filePath): static
{
// Read the configuration file.
$config = require $filePath;
// Merge the main configuration with the configuration we just read.
$this->config = array_merge($this->config, $config);
return $this;
}
/**
* Recursive function to get a configuration value.
* @param array $configuration Configuration array where to find the key.
* @param string[] $key Configuration key to find the value.
* @param string $fullKey Full configuration key.
* @return mixed The configuration value for the given key.
* @throws ConfigurationValueNotFoundException
*/
private static function _getValue(array $configuration, array $key, string $fullKey): mixed
{
// Get the currently looked key.
$currentKey = array_shift($key);
if (array_key_exists($currentKey, $configuration))
{ // Configuration key exists.
if (empty($key))
{ // We reached the value, returning it.
return $configuration[$currentKey];
}
else
{ // We must continue to search for the configuration value.
if (!is_array($configuration[$currentKey]))
// Cannot continue to search because currently pointed configuration value is not an array.
throw new ConfigurationValueNotFoundException($fullKey);
// Recursive call to get the requested value.
return static::_getValue($configuration[$currentKey], $key, $fullKey);
}
}
else
{ // Configuration key is missing.
throw new ConfigurationValueNotFoundException($fullKey);
}
}
/**
* Get a value from the loaded configuration.
* Throw an exception if the value is not present in configuration array.
* @param string $key Full configuration key, with "." to explore the configuration tree.
* @return mixed The value corresponding to the given key.
* @throws ConfigurationValueNotFoundException
*/
public function getValue(string $key): mixed
{
// Split the key.
$explodedKey = explode(".", $key);
// Call recursive retrieval of the value.
return static::_getValue($this->config, $explodedKey, $key);
}
/**
* Get a value from the loaded configuration.
* @param string $key Full configuration key, with "." to explore the configuration tree.
* @param mixed|null $default The default value, if there is none in the loaded configuration.
* @return mixed The value corresponding to the given key.
*/
public function val(string $key, mixed $default = null): mixed
{
try
{ // Trying to get the value corresponding to the given key.
return $this->getValue($key);
}
catch (ConfigurationValueNotFoundException $exception)
{ // Value could not be found, returning the default value.
return $default;
}
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Nest\Configuration;
/**
* Nest configuration service.
*/
trait ConfigurationService
{
/**
* The configuration instance.
* @var Configuration
*/
private Configuration $configuration;
protected function __nest__ConfigurationService(): void
{
// Initialize the configuration instance.
$this->configuration = new Configuration($this);
}
/**
* Configuration service.
* @return Configuration The configuration manager.
*/
public function configuration(): Configuration
{
return $this->configuration;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Nest\Configuration\Exceptions;
use Nest\Exceptions\Exception;
class ConfigurationException extends Exception
{
}

View file

@ -0,0 +1,21 @@
<?php
namespace Nest\Configuration\Exceptions;
use Throwable;
/**
* Exception thrown when a configuration value does not exists in configuration array.
*/
class ConfigurationValueNotFoundException extends ConfigurationException
{
/**
* @param string $key The configuration key not found.
* @param int $code [optional] The Exception code.
* @param null|Throwable $previous [optional] The previous throwable used for the exception chaining.
*/
public function __construct(public string $key, int $code = 0, ?Throwable $previous = null)
{
parent::__construct("Cannot find a configuration value for key \"$this->key\".", $code, $previous);
}
}