-
-
Save jfcherng/2fa6cccd68ad4cc84a31b974066db293 to your computer and use it in GitHub Desktop.
| MAINTENANCE_MODE=0 |
| <?php | |
| declare(strict_types=1); | |
| namespace App\EventSubscriber; | |
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
| use Symfony\Component\HttpFoundation\Response; | |
| use Symfony\Component\HttpKernel\Event\RequestEvent; | |
| use Symfony\Component\HttpKernel\KernelEvents; | |
| use Twig\Environment as TwigEnvironment; | |
| class KernelRequestSubscriber implements EventSubscriberInterface | |
| { | |
| private TwigEnvironment $twig; | |
| public function __construct(TwigEnvironment $twig) | |
| { | |
| $this->twig = $twig; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function getSubscribedEvents(): array | |
| { | |
| return [ | |
| KernelEvents::REQUEST => [ | |
| ['onMaintenance', \PHP_INT_MAX - 1000], | |
| ], | |
| ]; | |
| } | |
| public function onMaintenance(RequestEvent $event): void | |
| { | |
| /** @var bool $isMaintenance */ | |
| $isMaintenance = \filter_var($_ENV['MAINTENANCE_MODE'] ?? '0', \FILTER_VALIDATE_BOOLEAN); | |
| if ($isMaintenance) { | |
| $event->setResponse(new Response( | |
| $this->twig->render('maintenance.html.twig'), | |
| Response::HTTP_SERVICE_UNAVAILABLE, | |
| )); | |
| $event->stopPropagation(); | |
| } | |
| } | |
| } |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Site temporarily under maintenance</title> | |
| </head> | |
| <body> | |
| <h1>Site temporarily under maintenance</h1> | |
| <p>Sorry for the inconvenience, we will get back soon!</p> | |
| </body> | |
| </html> |
Hello @jfcherng, thanks for the gist!
It seems like i don't need to check if \PHP_SAPI === 'cli', my function seems to only execute when it's a web request anyway, any idea why?
Maybe because of symfony/runtime which separate the two types of requests? I'm not sure!
Bye!
Ah, yes. It's very likely that the KernelEvents::REQUEST hook won't be triggered when executed via CLI. Although I didn't do a real experiment but that sounds reasonable to me.
for modern symfony you can use this
public function __construct(
private TwigEnvironment $twig,
#[Autowire('%env(bool:MAINTENANCE_MODE)%')]
private $isMaintenance
) {
}Completing @sarim 's answer, a full example with with the usage of some newer Symfony and PHP features (Symfony 6.4+, PHP 8.2+) looks like this:
<?php
namespace App\EventListener;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Twig\Environment;
readonly class MaintenanceKernelRequestSubscriber
{
public function __construct(
#[Autowire('%env(bool:MAINTENANCE_MODE)%')]
private bool $isMaintenance,
private Environment $twig,
) {
}
#[AsEventListener(event: KernelEvents::REQUEST, priority: PHP_INT_MAX - 1000)]
public function onMaintenance(RequestEvent $event): void
{
if (!$this->isMaintenance) {
return;
}
$event->setResponse(new Response(
$this->twig->render('maintenance.html.twig'),
Response::HTTP_SERVICE_UNAVAILABLE,
));
$event->stopPropagation();
}
}Haha thanks you guys. Though I haven't written any PHP for years.
I can suggest an alternative that will let Symfony error rendering do the job for us
<?php
declare(strict_types=1);
namespace App\Maintenance;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* Detects if maintenance mode is enabled (via the environment variable of the same name).
* When this mode is enabled, an HTTP 503 exception is thrown.
* The error handling system is then responsible for rendering the appropriate response.
*/
final readonly class MaintenanceListener
{
public function __construct(
#[Autowire('%env(bool:MAINTENANCE)%')]
private bool $maintenance,
) {
}
#[AsEventListener(priority: \PHP_INT_MAX - 1000)]
public function __invoke(RequestEvent $event): void
{
if (!$this->maintenance) {
return;
}
// We definitely don't want to interfere with subrequests, especially since they are used for error rendering
if (!$event->isMainRequest()) {
return;
}
// We want to avoid blocking certain requests
$uri = $event->getRequest()->getRequestUri();
if (
\str_starts_with($uri, '/_profiler')
|| \str_starts_with($uri, '/_wdt')
|| \str_starts_with($uri, '/_error')
) {
return;
}
throw new HttpException(Response::HTTP_SERVICE_UNAVAILABLE);
}
}Then, following the documentation, you can create a templates/bundles/TwigBundle/Exception/error503.html.twig for your maintenance page
To enable the maintenance mode, set env variable
MAINTENANCE_MODE=1in the.env.localfile.