Initialize Nest configuration library.
This commit is contained in:
commit
a04d49f273
7 changed files with 313 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# IDEA
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Composer
|
||||||
|
vendor/
|
27
composer.json
Normal file
27
composer.json
Normal 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
56
composer.lock
generated
Normal 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
164
src/Configuration.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/ConfigurationService.php
Normal file
30
src/ConfigurationService.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
9
src/Exceptions/ConfigurationException.php
Normal file
9
src/Exceptions/ConfigurationException.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nest\Configuration\Exceptions;
|
||||||
|
|
||||||
|
use Nest\Exceptions\Exception;
|
||||||
|
|
||||||
|
class ConfigurationException extends Exception
|
||||||
|
{
|
||||||
|
}
|
21
src/Exceptions/ConfigurationValueNotFoundException.php
Normal file
21
src/Exceptions/ConfigurationValueNotFoundException.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue