Skip to content

Commit 7b6db77

Browse files
perf: memoize placeholder extraction per value (#154)
* perf: memoize placeholder extraction per value * test: assert placeholder cache is populated in cache-reuse test
1 parent ece2dcc commit 7b6db77

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

src/Validator/PlaceholderConsistencyValidator.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ class PlaceholderConsistencyValidator extends AbstractValidator implements Valid
3737
/** @var array<string, array<string, array{value: string, placeholders: array<string>}>> */
3838
protected array $keyData = [];
3939

40+
/**
41+
* Cache of extracted placeholders per value. The same value is processed
42+
* during analysis and again while rendering; memoizing avoids re-running
43+
* the placeholder regexes for it.
44+
*
45+
* @var array<string, array<string>>
46+
*/
47+
private array $placeholderCache = [];
48+
4049
public function processFile(ParserInterface $file): array
4150
{
4251
$keys = $file->extractKeys();
@@ -196,6 +205,7 @@ protected function resetState(): void
196205
{
197206
parent::resetState();
198207
$this->keyData = [];
208+
$this->placeholderCache = [];
199209
}
200210

201211
/**
@@ -211,6 +221,10 @@ protected function resetState(): void
211221
*/
212222
private function extractPlaceholders(string $value): array
213223
{
224+
if (isset($this->placeholderCache[$value])) {
225+
return $this->placeholderCache[$value];
226+
}
227+
214228
$placeholders = [];
215229

216230
// Symfony style: %parameter%
@@ -248,7 +262,7 @@ private function extractPlaceholders(string $value): array
248262
}
249263
}
250264

251-
return array_unique($placeholders);
265+
return $this->placeholderCache[$value] = array_unique($placeholders);
252266
}
253267

254268
/**

tests/src/Validator/PlaceholderConsistencyValidatorTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,38 @@ public function testRenderDetailedOutput(): void
363363
$this->assertStringContainsString('Hallo %username%!', $outputContent);
364364
}
365365

366+
public function testRenderDetailedOutputReusesCachedPlaceholders(): void
367+
{
368+
$logger = $this->createStub(LoggerInterface::class);
369+
$validator = new PlaceholderConsistencyValidator($logger);
370+
371+
// Both files carry the identical value, so highlighting the second cell
372+
// reuses the memoized placeholder extraction of the first.
373+
$issue = new Issue(
374+
'test.xlf',
375+
[
376+
'key' => 'shared.key',
377+
'files' => [
378+
'en.xlf' => ['value' => 'Hello %name%!', 'placeholders' => ['%name%']],
379+
'de.xlf' => ['value' => 'Hello %name%!', 'placeholders' => ['%name%']],
380+
],
381+
'inconsistencies' => ['dummy'],
382+
],
383+
'XliffParser',
384+
'PlaceholderConsistencyValidator',
385+
);
386+
387+
$output = new BufferedOutput();
388+
$validator->renderDetailedOutput($output, [$issue]);
389+
390+
$this->assertStringContainsString('Hello %name%!', $output->fetch());
391+
392+
$reflection = new ReflectionClass($validator);
393+
$cache = $reflection->getProperty('placeholderCache')->getValue($validator);
394+
$this->assertArrayHasKey('Hello %name%!', $cache);
395+
$this->assertSame(['%name%'], $cache['Hello %name%!']);
396+
}
397+
366398
public function testResetState(): void
367399
{
368400
$logger = $this->createStub(LoggerInterface::class);

0 commit comments

Comments
 (0)