chore: complete update

This commit is contained in:
2025-07-17 16:24:20 +02:00
parent 899227b0a4
commit 64a7051137
1300 changed files with 85570 additions and 2756 deletions

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Framework\Config;
use App\Framework\DateTime\Timezone;
final readonly class AppConfig
{
public function __construct(
public string $name = 'Framework App',
public string $version = '1.0.0',
public string $environment = 'production',
public bool $debug = false,
public Timezone $timezone = Timezone::DEFAULT,
public string $locale = 'en',
public EnvironmentType $type = EnvironmentType::DEV
) {}
}

View File

@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace App\Framework\Config;
class Configuration
{
private array $config = [];
/**
* Lädt Konfigurationen aus einer oder mehreren Quellen
*/
public function __construct(array $config = [])
{
$this->config = $config;
}
/**
* Lädt Konfigurationen aus PHP-Dateien
*/
public function loadFromFile(string $path): self
{
if (file_exists($path) && is_readable($path)) {
$fileConfig = require $path;
if (is_array($fileConfig)) {
$this->config = array_merge($this->config, $fileConfig);
}
}
return $this;
}
/**
* Lädt Konfigurationen aus einem Verzeichnis
*/
public function loadFromDirectory(string $directory): self
{
if (!is_dir($directory)) {
return $this;
}
foreach (glob($directory . '/*.php') as $file) {
$this->loadFromFile($file);
}
return $this;
}
/**
* Gibt einen Konfigurationswert zurück
*/
public function get(string $key, mixed $default = null): mixed
{
$keys = explode('.', $key);
$value = $this->config;
foreach ($keys as $segment) {
if (!is_array($value) || !array_key_exists($segment, $value)) {
return $default;
}
$value = $value[$segment];
}
return $value;
}
/**
* Setzt einen Konfigurationswert
*/
public function set(string $key, mixed $value): self
{
$keys = explode('.', $key);
$config = &$this->config;
foreach ($keys as $i => $segment) {
if ($i === count($keys) - 1) {
$config[$segment] = $value;
break;
}
if (!isset($config[$segment]) || !is_array($config[$segment])) {
$config[$segment] = [];
}
$config = &$config[$segment];
}
return $this;
}
/**
* Prüft, ob ein Konfigurationswert existiert
*/
public function has(string $key): bool
{
$keys = explode('.', $key);
$config = $this->config;
foreach ($keys as $segment) {
if (!is_array($config) || !array_key_exists($segment, $config)) {
return false;
}
$config = $config[$segment];
}
return true;
}
/**
* Gibt alle Konfigurationswerte zurück
*/
public function all(): array
{
return $this->config;
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Framework\Config;
enum EnvKey: string
{
case APP_DEBUG = 'APP_DEBUG';
case APP_ENV = 'APP_ENV';
case APP_TIMEZONE = 'APP_TIMEZONE';
case APP_LOCALE = 'APP_LOCALE';
}

View File

@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
namespace App\Framework\Config;
use App\Framework\Config\Exceptions\RequiredEnvironmentVariableException;
final readonly class Environment
{
public function __construct(
private array $variables = []
){}
public function get(EnvKey|string $key, mixed $default = null): mixed
{
$key = $this->keyToString($key);
// Priorität: 1. System ENV, 2. Loaded variables, 3. Default
return $_ENV[$key] ?? $_SERVER[$key] ?? getenv($key) ?: $this->variables[$key] ?? $default;
}
public function getRequired(EnvKey|string $key): mixed
{
$key = $this->keyToString($key);
$value = $this->get($key);
if ($value === null) {
throw new RequiredEnvironmentVariableException($key);
}
return $value;
}
public function getInt(EnvKey|string $key, int $default = 0): int
{
$key = $this->keyToString($key);
return (int) $this->get($key, $default);
}
public function getBool(EnvKey|string $key, bool $default = false): bool
{
$key = $this->keyToString($key);
$value = $this->get($key, $default);
if (is_string($value)) {
return match (strtolower($value)) {
'true', '1', 'yes', 'on' => true,
'false', '0', 'no', 'off' => false,
default => $default
};
}
return (bool) $value;
}
public function getString(EnvKey|string $key, string $default = ''): string
{
$key = $this->keyToString($key);
return (string) $this->get($key, $default);
}
public function getEnum(EnvKey|string $key, string $enumClass, \BackedEnum $default):object
{
$key = $this->keyToString($key);
return forward_static_call([$enumClass, 'tryFrom'], $this->get($key, $default));
#$enumClass::tryFrom($this->get($key, $default));
}
public function has(EnvKey|string $key): bool
{
$key = $this->keyToString($key);
return $this->get($key) !== null;
}
public function all(): array
{
return array_merge(
$_ENV,
$_SERVER,
$this->variables
);
}
/**
* Factory method für .env file loading
*/
public static function fromFile(string $envPath): self
{
$variables = [];
if (file_exists($envPath) && is_readable($envPath)) {
$lines = file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (str_starts_with(trim($line), '#') || !str_contains($line, '=')) {
continue;
}
[$name, $value] = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
// Remove quotes
if (str_starts_with($value, '"') && str_ends_with($value, '"')) {
$value = substr($value, 1, -1);
}
$variables[$name] = self::castValue($value);
}
}
return new self($variables);
}
private static function castValue(string $value): mixed
{
return match (strtolower($value)) {
'true' => true,
'false' => false,
'null' => null,
default => is_numeric($value) ? (str_contains($value, '.') ? (float) $value : (int) $value) : $value
};
}
public function keyToString(EnvKey|string $key):string
{
if(is_string($key)) {
return $key;
}
return $key->value;
}
/**
* Für Tests
*/
public function withVariable(string $key, mixed $value): self
{
$variables = $this->variables;
$variables[$key] = $value;
return new self($variables);
}
public function withVariables(array $variables): self
{
return new self(array_merge($this->variables, $variables));
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Framework\Config;
enum EnvironmentType: string
{
case DEV = 'development';
case PROD = 'production';
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Framework\Config\Exceptions;
use App\Framework\Exception\FrameworkException;
class RequiredEnvironmentVariableException extends FrameworkException
{
/**
* @param string $key
*/
public function __construct(string $key)
{
parent::__construct("Required environment variable '$key' is not set.");
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Framework\Config;
use App\Framework\Context\ContextType;
use App\Framework\DateTime\Timezone;
use App\Framework\DI\Container;
final readonly class TypedConfigInitializer
{
public function __construct(
private Environment $env,
){
#debug($env->getEnum('APP_ENV', EnvironmentType::class, EnvironmentType::PROD));
}
public function __invoke(Container $container): TypedConfiguration
{
$appConfig = $this->createAppConfig();
$container->instance(AppConfig::class, $appConfig);
return new TypedConfiguration(
app: $appConfig,
);
}
private function createAppConfig(): AppConfig
{
return new AppConfig(
environment: $this->env->getString(
key: EnvKey::APP_ENV,
default: 'production'
),
debug: $this->env->getBool(EnvKey::APP_DEBUG),
timezone: $this->env->getEnum(
key: EnvKey::APP_TIMEZONE,
enumClass: Timezone::class,
default: Timezone::EuropeBerlin
),
locale: $this->env->getString(EnvKey::APP_LOCALE, 'de'),
);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Framework\Config;
use App\Framework\Database\Config\DatabaseConfig;
final readonly class TypedConfiguration
{
public function __construct(
#public DatabaseConfig $database,
public AppConfig $app,
)
{
}
}