first commit
This commit is contained in:
213
src/Shared/Bootstrap.php
Normal file
213
src/Shared/Bootstrap.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Shared;
|
||||
|
||||
use App\Post\PostExtension;
|
||||
use App\Shared\Database\Provisioner;
|
||||
use App\Shared\Extension\AppExtension;
|
||||
use App\Shared\Extension\CsrfExtension;
|
||||
use App\Shared\Extension\SessionExtension;
|
||||
use DI\ContainerBuilder;
|
||||
use Dotenv\Dotenv;
|
||||
use PDO;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Slim\App;
|
||||
use Slim\Csrf\Guard;
|
||||
use Slim\Exception\HttpException;
|
||||
use Slim\Factory\AppFactory;
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Views\TwigMiddleware;
|
||||
use Throwable;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
private ?ContainerInterface $container = null;
|
||||
private ?App $app = null;
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function initialize(): App
|
||||
{
|
||||
$this->initializeInfrastructure();
|
||||
$this->runAutoProvisioningIfEnabled();
|
||||
|
||||
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
|
||||
{
|
||||
$dotenv = Dotenv::createImmutable(__DIR__.'/../..');
|
||||
$dotenv->load();
|
||||
$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 runAutoProvisioningIfEnabled(): void
|
||||
{
|
||||
$flag = strtolower(trim((string) ($_ENV['APP_AUTO_PROVISION'] ?? '')));
|
||||
$isDev = strtolower($_ENV['APP_ENV'] ?? 'production') === 'development';
|
||||
$enabled = $flag !== ''
|
||||
? in_array($flag, ['1', 'true', 'yes', 'on'], true)
|
||||
: $isDev;
|
||||
|
||||
if (!$enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
Provisioner::run($this->container->get(PDO::class));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
$errorHandler->setDefaultErrorHandler(
|
||||
function (
|
||||
ServerRequestInterface $request,
|
||||
Throwable $exception,
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails,
|
||||
) use ($isDev): ResponseInterface {
|
||||
if ($isDev) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$statusCode = 500;
|
||||
if ($exception instanceof HttpException) {
|
||||
$statusCode = $exception->getCode() ?: 500;
|
||||
}
|
||||
|
||||
$response = $this->app->getResponseFactory()->createResponse($statusCode);
|
||||
$twig = $this->container->get(Twig::class);
|
||||
|
||||
return $twig->render($response, 'pages/error.twig', [
|
||||
'status' => $statusCode,
|
||||
'message' => $statusCode === 404
|
||||
? 'La page demandée est introuvable.'
|
||||
: 'Une erreur inattendue s\'est produite.',
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user