initializeInfrastructure(); return $this->createHttpApp(); } public function initializeInfrastructure(): ContainerInterface { if ($this->container !== null) { return $this->container; } $this->checkDirectories(); $this->checkExtensions(); $this->loadEnvironment(); $this->buildContainer(); return $this->container; } public function createHttpApp(): App { if ($this->app !== null) { return $this->app; } $container = $this->initializeInfrastructure(); $this->app = AppFactory::createFromContainer($container); $this->registerMiddlewares(); $this->registerRoutes(); $this->configureErrorHandling(); return $this->app; } public function getContainer(): ContainerInterface { return $this->initializeInfrastructure(); } private function buildContainer(): void { $isDev = strtolower($_ENV['APP_ENV'] ?? 'production') === 'development'; $builder = new ContainerBuilder(); $builder->addDefinitions(__DIR__ . '/../../config/container.php'); if (!$isDev) { $builder->enableCompilation(__DIR__ . '/../../var/cache/di'); } $this->container = $builder->build(); } private function checkDirectories(): void { $dirs = [ __DIR__.'/../../var/cache/twig', __DIR__.'/../../var/cache/htmlpurifier', __DIR__.'/../../var/cache/di', __DIR__.'/../../var/logs', __DIR__.'/../../database', __DIR__.'/../../public/media', ]; foreach ($dirs as $dir) { if (!is_dir($dir) && !@mkdir($dir, 0755, true)) { throw new \RuntimeException("Impossible de créer le répertoire : {$dir}"); } } } private function checkExtensions(): void { if (!function_exists('imagewebp')) { throw new \RuntimeException( 'L\'extension PHP GD avec le support WebP est requise. ' . 'Installez le paquet php-gd (ex: apt install php-gd) puis redémarrez PHP.' ); } } private function loadEnvironment(): void { $rootDir = dirname(__DIR__, 2); $envPath = $rootDir . '/.env'; try { $dotenv = Dotenv::createImmutable($rootDir); $dotenv->load(); } catch (InvalidPathException $exception) { throw new \RuntimeException( sprintf( "Fichier .env introuvable a la racine du projet (%s). Copiez .env.example vers .env avant de demarrer l'application.", $envPath ), previous: $exception, ); } $dotenv->required(['APP_URL', 'ADMIN_USERNAME', 'ADMIN_EMAIL', 'ADMIN_PASSWORD']); date_default_timezone_set($_ENV['TIMEZONE'] ?? 'UTC'); $isDev = strtolower($_ENV['APP_ENV'] ?? 'production') === 'development'; if (!$isDev && ($_ENV['ADMIN_PASSWORD'] ?? '') === 'changeme123') { throw new \RuntimeException( 'ADMIN_PASSWORD doit être changé avant de démarrer en production.' ); } } private function registerMiddlewares(): void { $this->app->addBodyParsingMiddleware(); $twig = $this->container->get(Twig::class); $twig->addExtension($this->container->get(AppExtension::class)); $twig->addExtension($this->container->get(SessionExtension::class)); $twig->addExtension($this->container->get(PostExtension::class)); $this->app->add(TwigMiddleware::create($this->app, $twig)); $guard = new Guard($this->app->getResponseFactory()); $guard->setPersistentTokenMode(true); $twig->addExtension(new CsrfExtension($guard)); $this->app->add($guard); $container = $this->container; $this->app->add(function ( ServerRequestInterface $request, RequestHandlerInterface $handler, ) use ($container): ResponseInterface { DatabaseReadiness::assertProvisioned($container->get(PDO::class)); return $handler->handle($request); }); } private function registerRoutes(): void { Routes::register($this->app); } private function configureErrorHandling(): void { $isDev = strtolower($_ENV['APP_ENV'] ?? 'production') === 'development'; $logger = $this->container->get(LoggerInterface::class); $errorHandler = $this->app->addErrorMiddleware($isDev, true, true, $logger); $app = $this->app; $container = $this->container; $errorHandler->setDefaultErrorHandler( function ( ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails, ) use ($isDev, $app, $container): ResponseInterface { if ($isDev && !$exception instanceof DatabaseNotProvisionedException) { throw $exception; } $statusCode = 500; $message = 'Une erreur inattendue s\'est produite.'; if ($exception instanceof DatabaseNotProvisionedException) { $statusCode = 503; $message = $exception->getMessage(); } elseif ($exception instanceof HttpException) { $statusCode = $exception->getCode() ?: 500; $message = $statusCode === 404 ? 'La page demandée est introuvable.' : $message; } $response = $app->getResponseFactory()->createResponse($statusCode); $twig = $container->get(Twig::class); return $twig->render($response, 'pages/error.twig', [ 'status' => $statusCode, 'message' => $message, ]); } ); } }