feat: shortened and filename-safe request ids

This commit is contained in:
Björn Fromme
2026-03-16 12:03:00 +01:00
parent 1cc7a025a1
commit 33609ab534
6 changed files with 70 additions and 4 deletions
+2 -2
View File
@@ -1197,8 +1197,8 @@ Persists INFO+ level logs to `LogEntry` entity. Replaces message placeholders wi
When debug mode is enabled, API requests/responses are dumped to `var/bpn/`:
```
{request_id}_1_request.xml
{request_id}_1_response.xml
r-00m8z7k58a-8k1pvd9q_1_request.xml
r-00m8z7k58a-8k1pvd9q_1_response.xml
```
View via Admin Panel: Admin → Log → XML Dumps action.
+3 -1
View File
@@ -22,6 +22,7 @@ use App\BusProNet\Model\Travel;
use App\BusProNet\XmlParser\ApiResponseParser;
use App\Form\Model\BookingDto;
use App\Form\Model\RegistrationDto;
use App\Service\RequestIdGenerator;
use League\Flysystem\FilesystemException;
use League\Flysystem\FilesystemOperator;
use Psr\Log\LoggerInterface;
@@ -58,6 +59,7 @@ class ApiClient
private readonly LoggerInterface $logger,
private readonly BookingDataProcessor $bookingDataProcessor,
private readonly RequestStack $requestStack,
private readonly RequestIdGenerator $requestIdGenerator,
array $options,
) {
$this->config = $this->resolveOptions($options);
@@ -762,7 +764,7 @@ class ApiClient
private function getRequestId(): string
{
$baseId = $this->requestStack->getMainRequest()?->attributes->get('request_id')
?? date(DATE_ATOM);
?? $this->requestIdGenerator->generateBaseId();
return $baseId.'_'.++$this->requestCounter;
}
+7 -1
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\EventListener;
use App\Service\RequestIdGenerator;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
@@ -11,13 +12,18 @@ use Symfony\Component\HttpKernel\KernelEvents;
#[AsEventListener(event: KernelEvents::REQUEST, priority: 255)]
class RequestIdListener
{
public function __construct(
private readonly RequestIdGenerator $requestIdGenerator,
) {
}
public function __invoke(RequestEvent $event): void
{
if (false === $event->isMainRequest()) {
return;
}
$requestId = date(DATE_ATOM).uniqid();
$requestId = $this->requestIdGenerator->generateBaseId();
$event->getRequest()->attributes->set('request_id', $requestId);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Service;
class RequestIdGenerator
{
private const TIMESTAMP_LENGTH = 10;
private const RANDOM_LENGTH = 8;
public function generateBaseId(): string
{
$timestampMs = (int) floor(microtime(true) * 1000);
$timestampPart = str_pad(base_convert((string) $timestampMs, 10, 36), self::TIMESTAMP_LENGTH, '0', STR_PAD_LEFT);
$maxRandomValue = (36 ** self::RANDOM_LENGTH) - 1;
$randomPart = str_pad(base_convert((string) random_int(0, $maxRandomValue), 10, 36), self::RANDOM_LENGTH, '0', STR_PAD_LEFT);
return sprintf('r-%s-%s', $timestampPart, $randomPart);
}
}
+2
View File
@@ -8,6 +8,7 @@ use App\BusProNet\ApiClient;
use App\BusProNet\DataProcessor\BookingDataProcessor;
use App\BusProNet\Exception\ImmediateConnectionCloseException;
use App\BusProNet\XmlParser\ApiResponseParser;
use App\Service\RequestIdGenerator;
use League\Flysystem\FilesystemOperator;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
@@ -27,6 +28,7 @@ class ApiClientReceiveTest extends TestCase
$this->createMock(LoggerInterface::class),
$this->createMock(BookingDataProcessor::class),
new RequestStack(),
new RequestIdGenerator(),
[
'bpn_username' => 'test',
'bpn_password' => 'test',
+34
View File
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Tests\Service;
use App\Service\RequestIdGenerator;
use PHPUnit\Framework\TestCase;
class RequestIdGeneratorTest extends TestCase
{
public function testGeneratesFilenameSafeRequestId(): void
{
$generator = new RequestIdGenerator();
$requestId = $generator->generateBaseId();
self::assertMatchesRegularExpression('/^r-[0-9a-z]{10}-[0-9a-z]{8}$/', $requestId);
}
public function testGeneratedIdsAreSortableByTime(): void
{
$generator = new RequestIdGenerator();
$first = $generator->generateBaseId();
usleep(2_000);
$second = $generator->generateBaseId();
usleep(2_000);
$third = $generator->generateBaseId();
self::assertTrue($first < $second);
self::assertTrue($second < $third);
}
}