Files
michaelschiemer/src/Framework/Core/AppBootstrapper.php
Michael Schiemer 14900940c5 debug: Add Docker Secrets debugging to AppBootstrapper
- Add debug logging for REDIS_PASSWORD_FILE and REDIS_PASSWORD
- Check if REDIS_PASSWORD_FILE exists and has correct value
- Check if secret file exists and is readable
- Help diagnose why REDIS_PASSWORD_FILE is not in logs
2025-11-02 22:17:10 +01:00

271 lines
9.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Core;
use App\Framework\Config\EncryptedEnvLoader;
use App\Framework\Config\Environment;
use App\Framework\Config\EnvironmentType;
use App\Framework\Config\SecretManager;
use App\Framework\Config\TypedConfigInitializer;
use App\Framework\Config\TypedConfiguration;
use App\Framework\Console\ConsoleApplication;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Context\ExecutionContext;
use App\Framework\Core\Events\EventDispatcher;
use App\Framework\Core\Events\EventDispatcherInterface;
use App\Framework\DI\Container;
use App\Framework\DI\DefaultContainer;
use App\Framework\Encryption\EncryptionFactory;
use App\Framework\ErrorHandling\CliErrorHandler;
use App\Framework\ErrorHandling\ErrorHandler;
use App\Framework\ErrorHandling\ErrorHandlerManager;
use App\Framework\ExceptionHandling\ExceptionHandlerManager;
use App\Framework\Http\MiddlewareManager;
use App\Framework\Http\MiddlewareManagerInterface;
use App\Framework\Http\ResponseEmitter;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Random\RandomGenerator;
/**
* Verantwortlich für die grundlegende Initialisierung der Anwendung
*/
final readonly class AppBootstrapper
{
private Container $container;
private ContainerBootstrapper $bootstrapper;
public function __construct(
private string $basePath,
private PerformanceCollectorInterface $collector,
private MemoryMonitor $memoryMonitor = new MemoryMonitor,
) {
$this->container = new DefaultContainer();
$this->bootstrapper = new ContainerBootstrapper($this->container);
// Initialize environment with encryption support
$env = $this->initializeEnvironment();
error_log("------ ENVIRONMENT VARIABLES ------");
error_log(print_r($env->all(true), true));
error_log("------------------------------------");
// Debug: Check for specific Docker Secrets
error_log("------ DOCKER SECRETS DEBUG ------");
error_log("REDIS_PASSWORD_FILE exists: " . ($env->has('REDIS_PASSWORD_FILE') ? 'YES' : 'NO'));
error_log("REDIS_PASSWORD_FILE value: " . ($env->get('REDIS_PASSWORD_FILE') ?? 'NOT SET'));
error_log("REDIS_PASSWORD resolved: " . ($env->has('REDIS_PASSWORD') ? 'YES' : 'NO'));
error_log("REDIS_PASSWORD value: " . (empty($env->get('REDIS_PASSWORD')) ? 'EMPTY' : 'SET'));
// Check if file exists
$redisPasswordFile = $env->get('REDIS_PASSWORD_FILE');
if ($redisPasswordFile && is_string($redisPasswordFile)) {
error_log("REDIS_PASSWORD_FILE path: $redisPasswordFile");
error_log("File exists: " . (file_exists($redisPasswordFile) ? 'YES' : 'NO'));
error_log("File readable: " . (is_readable($redisPasswordFile) ? 'YES' : 'NO'));
if (file_exists($redisPasswordFile) && is_readable($redisPasswordFile)) {
$content = file_get_contents($redisPasswordFile);
error_log("File content length: " . strlen($content ?? ''));
}
}
error_log("------------------------------------");
// Make Environment available throughout the application
$this->container->instance(Environment::class, $env);
$typedConfig = new TypedConfigInitializer($env)($this->container);
$this->container->instance(TypedConfiguration::class, $typedConfig);
// ExecutionContext detection sollte das erste sein, das nach dem Instanziieren des containers passiert. noch bevor dem bootstrap des containers.
$executionContext = ExecutionContext::detect($typedConfig->app);
$this->container->instance(ExecutionContext::class, $executionContext);
// Register MemoryMonitor as singleton
$this->container->singleton(MemoryMonitor::class, $this->memoryMonitor);
// Only log context in development - production doesn't need this noise
//$envType = EnvironmentType::fromEnvironment($env);
//if ($envType->isDevelopment()) {
/*if($typedConfig->app->type->isDevelopment()) {
// Fehleranzeige für die Entwicklung aktivieren
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
spl_autoload_register(function ($class) {
if (empty($class)) {
error_log('Empty class name detected in autoloader. Stack trace: ' .
json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10)));
return false; // Don't throw, just log and continue
}
return false;
}, true, true);
}*/
}
public function bootstrapWeb(): ApplicationInterface
{
$this->bootstrap();
$this->registerWebErrorHandler();
$this->registerApplication();
$mm = $this->container->get(MiddlewareManager::class);
$this->container->instance(MiddlewareManagerInterface::class, $mm);
$ed = $this->container->get(EventDispatcher::class);
$this->container->instance(EventDispatcherInterface::class, $ed);
return $this->container->get(ApplicationInterface::class);
}
public function bootstrapConsole(): ConsoleApplication
{
$this->bootstrap();
$this->registerCliErrorHandler();
$this->registerConsoleApplication();
return $this->container->get(ConsoleApplication::class);
}
public function bootstrapWorker(): Container
{
$this->bootstrap();
$this->registerCliErrorHandler();
$ed = $this->container->get(EventDispatcher::class);
$this->container->instance(EventDispatcherInterface::class, $ed);
$consoleOutput = new ConsoleOutput();
$this->container->instance(ConsoleOutput::class, $consoleOutput);
return $this->container;
}
public function bootstrapWebSocket(): Container
{
$this->bootstrap();
$this->registerCliErrorHandler();
$consoleOutput = new ConsoleOutput();
$this->container->instance(ConsoleOutput::class, $consoleOutput);
return $this->container;
}
private function bootstrap(): void
{
$this->collector->startTiming('bootstrap', PerformanceCategory::SYSTEM);
$this->bootstrapper->bootstrap($this->basePath, $this->collector);
$this->collector->endTiming('bootstrap');
// Initialize secrets management after container is bootstrapped
$env = $this->container->get(Environment::class);
$this->initializeSecretsManagement($env);
// ErrorHandler wird jetzt kontextabhängig registriert
// $this->container->get(ErrorHandler::class)->register();
}
private function registerWebErrorHandler(): void
{
new ExceptionHandlerManager();
#var_dump('registerWebErrorHandler');
#$eh = $this->container->get(ErrorHandlerManager::class);
#$eh->register();
//$this->container->get(ErrorHandler::class)->register();
}
private function registerCliErrorHandler(): void
{
$output = $this->container->has(ConsoleOutput::class)
? $this->container->get(ConsoleOutput::class)
: new ConsoleOutput();
$cliErrorHandler = new CliErrorHandler($output);
$cliErrorHandler->register();
}
private function registerApplication(): void
{
$this->container->singleton(ApplicationInterface::class, function (Container $c) {
return new Application(
$c,
$c->get(ResponseEmitter::class),
$c->get(TypedConfiguration::class)
);
});
}
private function registerConsoleApplication(): void
{
$this->container->singleton(ConsoleApplication::class, function (Container $c) {
return new ConsoleApplication(
$c,
'console',
'My Console App',
null,
);
});
}
/**
* Initialize environment with smart priority handling
*
* Uses EnvironmentLoader which orchestrates:
* - Docker ENV vars loading (via $_ENV, $_SERVER)
* - .env file loading
* - Smart priority (Production: Docker ENV > .env, Development: .env > Docker ENV)
* - Automatic encryption support for .env.secrets
*/
private function initializeEnvironment(): Environment
{
$loader = new EncryptedEnvLoader();
return $loader->load($this->basePath);
}
/**
* Initialize secrets management after container is bootstrapped
*/
private function initializeSecretsManagement(Environment $env): void
{
$encryptionKey = $env->get('ENCRYPTION_KEY');
if ($encryptionKey === null) {
return; // No secrets management without encryption key
}
try {
$randomGenerator = $this->container->get(RandomGenerator::class);
$serverEnvironment = $this->container->get(ServerEnvironment::class);
$encryptionFactory = new EncryptionFactory($randomGenerator);
$encryption = $encryptionFactory->createBest($encryptionKey);
$secretManager = new SecretManager(
$env,
$encryption,
$serverEnvironment,
$randomGenerator
);
$this->container->instance(SecretManager::class, $secretManager);
} catch (\Throwable $e) {
error_log("Failed to initialize secrets management: " . $e->getMessage());
}
}
}