<?php
declare(strict_types=1);
namespace App\EventSubscriber;
use Doctrine\DBAL\Connection;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
final class DbKeepAliveSubscriber implements EventSubscriberInterface
{
private ManagerRegistry $doctrine;
public function __construct(ManagerRegistry $doctrine)
{
$this->doctrine = $doctrine;
}
public static function getSubscribedEvents(): array
{
// Très tôt pour précéder les accès DB (ChannelResolver, etc.)
return [KernelEvents::REQUEST => ['onKernelRequest', 2048]];
}
public function onKernelRequest(RequestEvent $event): void
{
// Symfony >=5.3
if (method_exists($event, 'isMainRequest') && !$event->isMainRequest()) {
return;
}
// Compat <5.3
if (method_exists($event, 'isMasterRequest') && !$event->isMasterRequest()) {
return;
}
foreach ($this->doctrine->getConnections() as $connection) {
$this->pingOrReconnect($connection);
// (optionnel) poser quelques paramètres de session utiles :
$this->initMysqlSession($connection);
}
}
private function pingOrReconnect(Connection $conn): void
{
try {
if (method_exists($conn, 'ping')) {
if (!$conn->ping()) {
$conn->close();
$conn->connect();
}
} else {
// fallback vieux DBAL
$conn->executeQuery('SELECT 1');
}
} catch (\Throwable $e) {
$conn->close();
$conn->connect();
}
}
private function initMysqlSession(Connection $conn): void
{
try {
// Sécurisé: commandes simples et indépendantes
$conn->executeStatement("SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'");
// Si autorisé côté serveur, augmente les timeouts session
$conn->executeStatement("SET SESSION wait_timeout = 28800");
$conn->executeStatement("SET SESSION interactive_timeout = 28800");
} catch (\Throwable $e) {
// ignorer si non supporté par l'hébergeur
}
}
}