Better code organization
This commit is contained in:
109
public/index.php
109
public/index.php
@@ -4,116 +4,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
require __DIR__ . '/../vendor/autoload.php';
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
use Dotenv\Dotenv;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use Slim\Views\TwigMiddleware;
|
|
||||||
use Slim\Views\Twig;
|
|
||||||
use Slim\Csrf\Guard;
|
|
||||||
use Medoo\Medoo;
|
|
||||||
use App\Controllers\PostController;
|
|
||||||
use App\Repositories\PostRepository;
|
|
||||||
use App\Services\HtmlSanitizer;
|
|
||||||
use App\Services\HtmlPurifierFactory;
|
|
||||||
use App\Services\CsrfExtension;
|
|
||||||
use App\Database\Migrator;
|
|
||||||
use App\Bootstrap;
|
use App\Bootstrap;
|
||||||
use App\Routes;
|
|
||||||
use App\Config;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Démarrer la session PHP
|
// Démarrer la session PHP
|
||||||
// ============================================
|
|
||||||
|
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
// ============================================
|
// Initialiser et exécuter l'application
|
||||||
// Vérifier les répertoires
|
$app = Bootstrap::create()->initialize();
|
||||||
// ============================================
|
|
||||||
|
|
||||||
Bootstrap::checkDirectories();
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Charger les variables d'environnement
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
|
|
||||||
$dotenv->load();
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Configuration
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$env = $_ENV['APP_ENV'] ?? 'production';
|
|
||||||
$isDev = strtolower($env) === 'development';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Initialisation de l'application Slim
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$app = AppFactory::create();
|
|
||||||
$responseFactory = $app->getResponseFactory();
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Initialisation des services
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
// CSRF Guard (middleware)
|
|
||||||
$csrf = new Guard($responseFactory);
|
|
||||||
|
|
||||||
// Twig
|
|
||||||
$twig = Twig::create(
|
|
||||||
__DIR__ . '/../views',
|
|
||||||
['cache' => Config::getTwigCache($isDev)]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ajouter l'extension CSRF à Twig
|
|
||||||
$twig->addExtension(new CsrfExtension($csrf));
|
|
||||||
|
|
||||||
// Medoo (SQLite)
|
|
||||||
$dbFile = Config::getDatabasePath();
|
|
||||||
$db = new Medoo([
|
|
||||||
'type' => 'sqlite',
|
|
||||||
'database' => $dbFile,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Exécuter les migrations
|
|
||||||
Migrator::run($db);
|
|
||||||
|
|
||||||
// HtmlPurifier (créé via la factory)
|
|
||||||
$htmlPurifierCacheDir = __DIR__ . '/../var/cache/htmlpurifier';
|
|
||||||
$htmlPurifier = HtmlPurifierFactory::create($htmlPurifierCacheDir);
|
|
||||||
|
|
||||||
// HtmlSanitizer (reçoit HTMLPurifier injecté)
|
|
||||||
$htmlSanitizer = new HtmlSanitizer($htmlPurifier);
|
|
||||||
|
|
||||||
// PostRepository
|
|
||||||
$postRepository = new PostRepository($db);
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Middleware
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->add(TwigMiddleware::create($app, $twig));
|
|
||||||
|
|
||||||
// Enregistrer le middleware CSRF pour toutes les routes
|
|
||||||
$app->add($csrf);
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Routes
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$controller = new PostController($twig, $postRepository, $htmlSanitizer);
|
|
||||||
Routes::register($app, $controller);
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Error Handling
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$errorMiddleware = $app->addErrorMiddleware($isDev, $isDev, $isDev);
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Run
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
$app->run();
|
$app->run();
|
||||||
|
|||||||
@@ -4,9 +4,69 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
use Slim\App;
|
||||||
|
use Slim\Views\TwigMiddleware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classe d'amorçage de l'application.
|
||||||
|
* Orchestre l'initialisation complète de l'application Slim.
|
||||||
|
*/
|
||||||
final class Bootstrap
|
final class Bootstrap
|
||||||
{
|
{
|
||||||
public static function checkDirectories(): void
|
private Container $container;
|
||||||
|
private App $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une nouvelle instance de Bootstrap.
|
||||||
|
*/
|
||||||
|
public static function create(): self
|
||||||
|
{
|
||||||
|
return new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
$this->container = new Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise complètement l'application et retourne l'instance Slim.
|
||||||
|
*
|
||||||
|
* @return App L'application Slim prête à être exécutée
|
||||||
|
*/
|
||||||
|
public function initialize(): App
|
||||||
|
{
|
||||||
|
// 1. Vérifier les répertoires
|
||||||
|
$this->checkDirectories();
|
||||||
|
|
||||||
|
// 2. Charger les variables d'environnement
|
||||||
|
$this->loadEnvironment();
|
||||||
|
|
||||||
|
// 3. Initialiser l'application Slim
|
||||||
|
$this->app = $this->container->get('app');
|
||||||
|
|
||||||
|
// 4. Exécuter les migrations
|
||||||
|
$this->container->get('migrator');
|
||||||
|
|
||||||
|
// 5. Enregistrer les middlewares
|
||||||
|
$this->registerMiddlewares();
|
||||||
|
|
||||||
|
// 6. Enregistrer les routes
|
||||||
|
$this->registerRoutes();
|
||||||
|
|
||||||
|
// 7. Configurer la gestion des erreurs
|
||||||
|
$this->configureErrorHandling();
|
||||||
|
|
||||||
|
return $this->app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie et crée les répertoires nécessaires.
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException Si un répertoire ne peut pas être créé
|
||||||
|
*/
|
||||||
|
private function checkDirectories(): void
|
||||||
{
|
{
|
||||||
$dirs = [
|
$dirs = [
|
||||||
__DIR__ . '/../var/cache/twig',
|
__DIR__ . '/../var/cache/twig',
|
||||||
@@ -23,4 +83,63 @@ final class Bootstrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charge les variables d'environnement depuis le fichier .env
|
||||||
|
*/
|
||||||
|
private function loadEnvironment(): void
|
||||||
|
{
|
||||||
|
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistre tous les middlewares de l'application.
|
||||||
|
*/
|
||||||
|
private function registerMiddlewares(): void
|
||||||
|
{
|
||||||
|
// Middleware de parsing du body
|
||||||
|
$this->app->addBodyParsingMiddleware();
|
||||||
|
|
||||||
|
// Twig et extension CSRF
|
||||||
|
$twig = $this->container->get('twig');
|
||||||
|
$csrfExtension = $this->container->get('csrfExtension');
|
||||||
|
$twig->addExtension($csrfExtension);
|
||||||
|
$this->app->add(TwigMiddleware::create($this->app, $twig));
|
||||||
|
|
||||||
|
// CSRF Guard
|
||||||
|
$csrf = $this->container->get('csrf');
|
||||||
|
$this->app->add($csrf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistre toutes les routes de l'application.
|
||||||
|
*/
|
||||||
|
private function registerRoutes(): void
|
||||||
|
{
|
||||||
|
$controller = $this->container->get('postController');
|
||||||
|
Routes::register($this->app, $controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure la gestion des erreurs en fonction de l'environnement.
|
||||||
|
*/
|
||||||
|
private function configureErrorHandling(): void
|
||||||
|
{
|
||||||
|
$config = $this->container->get('config');
|
||||||
|
$isDev = $config['isDev'];
|
||||||
|
|
||||||
|
$this->app->addErrorMiddleware($isDev, $isDev, $isDev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne le conteneur de services.
|
||||||
|
* Utile pour accéder aux services en dehors du Bootstrap.
|
||||||
|
*
|
||||||
|
* @return Container
|
||||||
|
*/
|
||||||
|
public function getContainer(): Container
|
||||||
|
{
|
||||||
|
return $this->container;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
187
src/Container.php
Normal file
187
src/Container.php
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Slim\App;
|
||||||
|
use Slim\Factory\AppFactory;
|
||||||
|
use Slim\Views\Twig;
|
||||||
|
use Slim\Csrf\Guard;
|
||||||
|
use Medoo\Medoo;
|
||||||
|
use App\Controllers\PostController;
|
||||||
|
use App\Repositories\PostRepository;
|
||||||
|
use App\Services\HtmlSanitizer;
|
||||||
|
use App\Services\HtmlPurifierFactory;
|
||||||
|
use App\Services\CsrfExtension;
|
||||||
|
use App\Database\Migrator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conteneur de services pour l'injection de dépendances.
|
||||||
|
* Gère l'instanciation et la mise en cache de tous les services.
|
||||||
|
*/
|
||||||
|
final class Container implements ContainerInterface
|
||||||
|
{
|
||||||
|
/** @var array<string, mixed> Services instanciés et en cache */
|
||||||
|
private array $services = [];
|
||||||
|
|
||||||
|
/** @var array<string, callable> Factories pour créer les services */
|
||||||
|
private array $factories = [];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->registerFactories();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistre toutes les factories de services.
|
||||||
|
*/
|
||||||
|
private function registerFactories(): void
|
||||||
|
{
|
||||||
|
// ============================================
|
||||||
|
// Configuration
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['config'] = function (): array {
|
||||||
|
return [
|
||||||
|
'env' => $_ENV['APP_ENV'] ?? 'production',
|
||||||
|
'isDev' => strtolower($_ENV['APP_ENV'] ?? 'production') === 'development',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Application Slim
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['app'] = function (): App {
|
||||||
|
return AppFactory::create();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Base de données
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['database'] = function (): Medoo {
|
||||||
|
return new Medoo([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'database' => Config::getDatabasePath(),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Migrations
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['migrator'] = function (): Migrator {
|
||||||
|
$db = $this->get('database');
|
||||||
|
Migrator::run($db);
|
||||||
|
return new Migrator();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Twig (Template Engine)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['twig'] = function (): Twig {
|
||||||
|
$config = $this->get('config');
|
||||||
|
return Twig::create(
|
||||||
|
__DIR__ . '/../views',
|
||||||
|
['cache' => Config::getTwigCache($config['isDev'])]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CSRF Guard
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['csrf'] = function (): Guard {
|
||||||
|
$app = $this->get('app');
|
||||||
|
return new Guard($app->getResponseFactory());
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CSRF Extension pour Twig
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['csrfExtension'] = function (): CsrfExtension {
|
||||||
|
$csrf = $this->get('csrf');
|
||||||
|
return new CsrfExtension($csrf);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// HTML Purifier
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['htmlPurifier'] = function () {
|
||||||
|
$cacheDir = __DIR__ . '/../var/cache/htmlpurifier';
|
||||||
|
return HtmlPurifierFactory::create($cacheDir);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// HTML Sanitizer
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['htmlSanitizer'] = function (): HtmlSanitizer {
|
||||||
|
$htmlPurifier = $this->get('htmlPurifier');
|
||||||
|
return new HtmlSanitizer($htmlPurifier);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Repositories
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['postRepository'] = function (): PostRepository {
|
||||||
|
$db = $this->get('database');
|
||||||
|
return new PostRepository($db);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Controllers
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
$this->factories['postController'] = function (): PostController {
|
||||||
|
return new PostController(
|
||||||
|
$this->get('twig'),
|
||||||
|
$this->get('postRepository'),
|
||||||
|
$this->get('htmlSanitizer')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un service du conteneur.
|
||||||
|
* Les services sont instanciés une seule fois et mis en cache.
|
||||||
|
*
|
||||||
|
* @param string $id Identifiant du service
|
||||||
|
* @return mixed
|
||||||
|
* @throws \Exception Si le service n'existe pas
|
||||||
|
*/
|
||||||
|
public function get(string $id): mixed
|
||||||
|
{
|
||||||
|
// Retourner le service en cache s'il existe
|
||||||
|
if (isset($this->services[$id])) {
|
||||||
|
return $this->services[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que la factory existe
|
||||||
|
if (!isset($this->factories[$id])) {
|
||||||
|
throw new \Exception("Service '{$id}' not found in container");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer et mettre en cache le service
|
||||||
|
$this->services[$id] = $this->factories[$id]();
|
||||||
|
return $this->services[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si un service existe dans le conteneur.
|
||||||
|
*
|
||||||
|
* @param string $id Identifiant du service
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has(string $id): bool
|
||||||
|
{
|
||||||
|
return isset($this->factories[$id]) || isset($this->services[$id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user