<?php
declare(strict_types=1);
namespace App\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
final class BuildVersionListener
{
private const MESSAGE_TEMPLATE_INVALID_VERSION = 'Version conflict: version >= %s expected.';
private const MESSAGE_TEMPLATE_INVALID_BUILD = 'Version conflict: build >= %s expected.';
public function __construct(
private readonly string $minVersion,
private readonly string $headerName,
private readonly array $paths
) {
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if (
!$request->headers->has($this->headerName)
|| !$this->matchPath($request->getPathInfo())
) {
return;
}
$currentVersion = $this->parseVersion($request->headers->get($this->headerName));
$minVersion = $this->parseVersion($this->minVersion);
switch (true) {
case version_compare($currentVersion['version'], $minVersion['version'], '<'):
$message = sprintf(self::MESSAGE_TEMPLATE_INVALID_VERSION, $minVersion['version']);
break;
case $currentVersion['build'] < $minVersion['build']:
$message = sprintf(self::MESSAGE_TEMPLATE_INVALID_BUILD, $minVersion['build']);
break;
default:
return;
}
$response = new Response($message, Response::HTTP_CONFLICT);
$event->setResponse($response);
}
private function matchPath(string $pathInfo): bool
{
foreach ($this->paths as $path) {
$pattern = sprintf('#%s#ui', $path);
if (preg_match($pattern, $pathInfo)) {
return true;
}
}
return false;
}
private function parseVersion(string $version): array
{
$data = [
'version' => $version,
'build' => 0,
];
if (preg_match('/^(.*)-(\d+)$/ui', $version, $matches)) {
$data = [
'version' => $matches[1],
'build' => (int)$matches[2],
];
}
return $data;
}
}