safeLoad(); } $storagePath = self::env('STORAGE_PATH', $basePath . '/storage'); if (!is_dir($storagePath)) { mkdir($storagePath, 0775, true); } $databasePath = self::env('DATABASE_PATH', $storagePath . '/database.sqlite'); if (!self::isAbsolutePath($databasePath)) { $databasePath = $basePath . '/' . ltrim($databasePath, '/'); } $jwtSecret = self::env('JWT_SECRET') ?? self::resolveSecret($storagePath . '/secret.key'); $jwtTtl = (int) (self::env('JWT_TTL') ?? '86400'); $displayErrors = filter_var(self::env('APP_DEBUG', 'false'), FILTER_VALIDATE_BOOL); $appUrl = rtrim(self::env('APP_URL', 'http://localhost:5173'), '/'); $mailLogPath = self::env('MAIL_LOG_PATH', $storagePath . '/mail.log'); if (!self::isAbsolutePath($mailLogPath)) { $mailLogPath = $basePath . '/' . ltrim($mailLogPath, '/'); } $mail = new MailConfig( transport: strtolower(self::env('MAIL_TRANSPORT', 'mail')), fromAddress: self::env('MAIL_FROM', 'no-reply@todo.test'), fromName: self::env('MAIL_FROM_NAME', 'Todo List'), logPath: $mailLogPath, smtpHost: self::env('MAIL_SMTP_HOST'), smtpPort: (int) (self::env('MAIL_SMTP_PORT') ?? '587'), smtpUsername: self::env('MAIL_SMTP_USERNAME'), smtpPassword: self::env('MAIL_SMTP_PASSWORD'), smtpEncryption: strtolower(self::env('MAIL_SMTP_ENCRYPTION', 'tls')), ); return new self($databasePath, $jwtSecret, $jwtTtl, $displayErrors, $appUrl, $mail); } private static function env(string $key, ?string $default = null): ?string { $value = $_ENV[$key] ?? $_SERVER[$key] ?? getenv($key); if ($value === false || $value === null || $value === '') { return $default; } return (string) $value; } private static function isAbsolutePath(string $path): bool { return str_starts_with($path, '/') || preg_match('#^[A-Za-z]:[\\\\/]#', $path) === 1; } /** * Return the persisted signing secret, generating and storing one on first run * so local development works with zero configuration. */ private static function resolveSecret(string $path): string { if (is_file($path)) { return trim((string) file_get_contents($path)); } $secret = bin2hex(random_bytes(32)); file_put_contents($path, $secret); @chmod($path, 0600); return $secret; } }