Files
slim-blog/config/container.php
2026-03-16 14:21:31 +01:00

203 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
/**
* Définitions PHP-DI.
*
* Ce fichier déclare uniquement ce que l'autowiring ne peut pas résoudre seul :
* - Bindings interface → classe concrète
* - Classes nécessitant des paramètres scalaires issus de .env ou de chemins filesystem
*
* Tout le reste (services, repositories, contrôleurs dont toutes les dépendances
* sont typées sur des interfaces) est résolu automatiquement par l'autowiring.
*/
use App\Auth\AuthService;
use App\Auth\AuthServiceInterface;
use App\Auth\LoginAttemptRepository;
use App\Auth\LoginAttemptRepositoryInterface;
use App\Auth\PasswordResetController;
use App\Auth\PasswordResetRepository;
use App\Auth\PasswordResetRepositoryInterface;
use App\Auth\PasswordResetService;
use App\Auth\PasswordResetServiceInterface;
use App\Category\Application\CategoryApplicationService;
use App\Category\CategoryRepository;
use App\Category\CategoryRepositoryInterface;
use App\Category\CategoryService;
use App\Category\CategoryServiceInterface;
use App\Category\Infrastructure\PdoCategoryRepository;
use App\Media\MediaRepository;
use App\Media\MediaRepositoryInterface;
use App\Media\MediaService;
use App\Media\MediaServiceInterface;
use App\Post\Application\PostApplicationService;
use App\Post\PostRepository;
use App\Post\PostRepositoryInterface;
use App\Post\PostService;
use App\Post\PostServiceInterface;
use App\Post\Infrastructure\PdoPostRepository;
use App\Post\RssController;
use App\Shared\Config;
use App\Shared\Extension\AppExtension;
use App\Shared\Html\HtmlPurifierFactory;
use App\Shared\Html\HtmlSanitizer;
use App\Shared\Html\HtmlSanitizerInterface;
use App\Shared\Http\ClientIpResolver;
use App\Shared\Http\FlashService;
use App\Shared\Http\FlashServiceInterface;
use App\Shared\Http\SessionManager;
use App\Shared\Http\SessionManagerInterface;
use App\Shared\Mail\MailService;
use App\Shared\Mail\MailServiceInterface;
use App\User\Application\UserApplicationService;
use App\User\Infrastructure\PdoUserRepository;
use App\User\UserRepositoryInterface;
use App\User\UserServiceInterface;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Slim\Views\Twig;
use function DI\autowire;
use function DI\factory;
return [
// ── Bindings interface → implémentation ──────────────────────────────────
AuthServiceInterface::class => autowire(AuthService::class),
PostServiceInterface::class => autowire(PostApplicationService::class),
UserServiceInterface::class => autowire(UserApplicationService::class),
CategoryServiceInterface::class => autowire(CategoryApplicationService::class),
CategoryRepositoryInterface::class => autowire(PdoCategoryRepository::class),
MediaRepositoryInterface::class => autowire(MediaRepository::class),
PostRepositoryInterface::class => autowire(PdoPostRepository::class),
UserRepositoryInterface::class => autowire(PdoUserRepository::class),
LoginAttemptRepositoryInterface::class => autowire(LoginAttemptRepository::class),
PasswordResetRepositoryInterface::class => autowire(PasswordResetRepository::class),
PasswordResetServiceInterface::class => autowire(PasswordResetService::class),
FlashServiceInterface::class => autowire(FlashService::class),
SessionManagerInterface::class => autowire(SessionManager::class),
HtmlSanitizerInterface::class => autowire(HtmlSanitizer::class),
// ── Infrastructure ────────────────────────────────────────────────────────
LoggerInterface::class => factory(function (): LoggerInterface {
$isDev = strtolower($_ENV['APP_ENV'] ?? 'production') === 'development';
$logger = new Logger('slim-blog');
$level = $isDev ? Level::Debug : Level::Warning;
$logger->pushHandler(
new StreamHandler(dirname(__DIR__) . '/var/logs/app.log', $level)
);
return $logger;
}),
PDO::class => factory(function (): PDO {
$pdo = new PDO('sqlite:' . Config::getDatabasePath(), options: [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// strip_tags() PHP exposée comme fonction SQLite pour les triggers FTS5
$pdo->sqliteCreateFunction('strip_tags', 'strip_tags', 1);
$pdo->exec('PRAGMA journal_mode=WAL');
// Attend jusqu'à 3s avant d'échouer sur une contention en écriture
$pdo->exec('PRAGMA busy_timeout=3000');
// Réduit les fsync sans sacrifier la cohérence en WAL mode
$pdo->exec('PRAGMA synchronous=NORMAL');
// Active l'application réelle des contraintes de clé étrangère.
// Sans ce pragma, les ON DELETE SET NULL / CASCADE déclarés dans les
// migrations sont enregistrés dans le schéma mais silencieusement ignorés
// à l'exécution — SQLite désactive les FK par défaut pour la compatibilité.
$pdo->exec('PRAGMA foreign_keys=ON');
return $pdo;
}),
Twig::class => factory(function (): Twig {
$isDev = strtolower($_ENV['APP_ENV'] ?? 'production') === 'development';
return Twig::create(
dirname(__DIR__) . '/views',
['cache' => Config::getTwigCache($isDev)],
);
}),
\HTMLPurifier::class => factory(function (): \HTMLPurifier {
return HtmlPurifierFactory::create(dirname(__DIR__) . '/var/cache/htmlpurifier');
}),
// ── Services nécessitant une configuration .env ───────────────────────────
MailServiceInterface::class => factory(function (Twig $twig): MailServiceInterface {
return new MailService(
twig: $twig,
host: $_ENV['MAIL_HOST'] ?? '',
port: (int) ($_ENV['MAIL_PORT'] ?? 587),
username: $_ENV['MAIL_USERNAME'] ?? '',
password: $_ENV['MAIL_PASSWORD'] ?? '',
encryption: strtolower($_ENV['MAIL_ENCRYPTION'] ?? 'tls'),
from: $_ENV['MAIL_FROM'] ?? '',
fromName: $_ENV['MAIL_FROM_NAME'] ?? 'Slim Blog',
);
}),
MediaServiceInterface::class => factory(
function (MediaRepositoryInterface $mediaRepository, PostRepositoryInterface $postRepository): MediaServiceInterface {
return new MediaService(
mediaRepository: $mediaRepository,
postRepository: $postRepository,
uploadDir: dirname(__DIR__) . '/public/media',
uploadUrl: '/media',
maxSize: (int) ($_ENV['UPLOAD_MAX_SIZE'] ?? 5 * 1024 * 1024),
);
}
),
// ── Contrôleurs nécessitant des paramètres scalaires ─────────────────────
RssController::class => factory(
function (PostServiceInterface $postService): RssController {
return new RssController(
$postService,
rtrim($_ENV['APP_URL'] ?? 'http://localhost', '/'),
$_ENV['APP_NAME'] ?? 'Slim Blog',
);
}
),
PasswordResetController::class => factory(
function (
Twig $twig,
PasswordResetServiceInterface $passwordResetService,
AuthServiceInterface $authService,
FlashServiceInterface $flash,
ClientIpResolver $clientIpResolver,
): PasswordResetController {
return new PasswordResetController(
$twig,
$passwordResetService,
$authService,
$flash,
$clientIpResolver,
rtrim($_ENV['APP_URL'] ?? 'http://localhost', '/'),
);
}
),
ClientIpResolver::class => factory(function (): ClientIpResolver {
$trusted = array_filter(array_map('trim', explode(',', (string) ($_ENV['TRUSTED_PROXIES'] ?? ''))));
return new ClientIpResolver($trusted);
}),
AppExtension::class => factory(function (): AppExtension {
return new AppExtension(rtrim($_ENV['APP_URL'] ?? 'http://localhost', '/'));
}),
];