94 lines
2.7 KiB
PHP
94 lines
2.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Phred\Support;
|
|
|
|
final class Config
|
|
{
|
|
/** @var array<string,mixed>|null */
|
|
private static ?array $store = null;
|
|
|
|
/**
|
|
* Get configuration value with precedence:
|
|
* 1) Environment variables (UPPER_CASE or dot.notation translated)
|
|
* 2) Loaded config files from config/*.php, accessible via dot.notation (e.g., app.env)
|
|
* 3) Provided $default
|
|
*/
|
|
public static function get(string $key, mixed $default = null): mixed
|
|
{
|
|
// 1) Environment lookup (supports dot.notation by converting to UPPER_SNAKE)
|
|
$envKey = strtoupper(str_replace('.', '_', $key));
|
|
$value = getenv($envKey);
|
|
if ($value !== false) {
|
|
return $value;
|
|
}
|
|
if (isset($_SERVER[$envKey])) {
|
|
return $_SERVER[$envKey];
|
|
}
|
|
if (isset($_ENV[$envKey])) {
|
|
return $_ENV[$envKey];
|
|
}
|
|
|
|
// 2) Config files (lazy load once)
|
|
self::ensureLoaded();
|
|
if (self::$store) {
|
|
$fromStore = self::getFromStore($key);
|
|
if ($fromStore !== null) {
|
|
return $fromStore;
|
|
}
|
|
}
|
|
|
|
// 3) Default
|
|
return $default;
|
|
}
|
|
|
|
private static function ensureLoaded(): void
|
|
{
|
|
if (self::$store !== null) {
|
|
return;
|
|
}
|
|
self::$store = [];
|
|
$root = getcwd();
|
|
$configDir = $root . DIRECTORY_SEPARATOR . 'config';
|
|
if (!is_dir($configDir)) {
|
|
return; // no config directory; keep empty store
|
|
}
|
|
foreach (glob($configDir . '/*.php') ?: [] as $file) {
|
|
$key = basename($file, '.php');
|
|
try {
|
|
$data = require $file;
|
|
if (is_array($data)) {
|
|
self::$store[$key] = $data;
|
|
}
|
|
} catch (\Throwable) {
|
|
// ignore malformed config files to avoid breaking runtime
|
|
}
|
|
}
|
|
}
|
|
|
|
private static function getFromStore(string $key): mixed
|
|
{
|
|
// dot.notation: first segment is file key, remaining traverse array
|
|
if (str_contains($key, '.')) {
|
|
$parts = explode('.', $key);
|
|
$rootKey = array_shift($parts);
|
|
if ($rootKey === null || !isset(self::$store[$rootKey])) {
|
|
return null;
|
|
}
|
|
$cursor = self::$store[$rootKey];
|
|
foreach ($parts as $p) {
|
|
if (is_array($cursor) && array_key_exists($p, $cursor)) {
|
|
$cursor = $cursor[$p];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return $cursor;
|
|
}
|
|
|
|
// non-dotted: try exact file key
|
|
return self::$store[$key] ?? null;
|
|
}
|
|
}
|