Fix unbounded memoisation of IntlDateFormatter / NumberFormatter

This commit is contained in:
Alexandre Daubois
2026-05-05 14:12:17 +02:00
committed by Fabien Potencier
parent b675555ea2
commit 6add9066fc
2 changed files with 39 additions and 0 deletions
+8
View File
@@ -145,6 +145,8 @@ final class IntlExtension extends AbstractExtension
'monetary_grouping_separator' => \NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL,
];
private const MAX_CACHED_FORMATTERS = 100;
private $dateFormatters = [];
private $numberFormatters = [];
private $dateFormatterPrototype;
@@ -441,6 +443,9 @@ final class IntlExtension extends AbstractExtension
$hash = $locale.'|'.$dateFormatValue.'|'.$timeFormatValue.'|'.$timezoneName.'|'.$calendar.'|'.$pattern;
if (!isset($this->dateFormatters[$hash])) {
if (\count($this->dateFormatters) >= self::MAX_CACHED_FORMATTERS) {
array_shift($this->dateFormatters);
}
$this->dateFormatters[$hash] = new \IntlDateFormatter($locale, $dateFormatValue, $timeFormatValue, $timezone, $calendar, $pattern);
}
@@ -487,6 +492,9 @@ final class IntlExtension extends AbstractExtension
$hash = $locale.'|'.$style.'|'.json_encode($attrs).'|'.json_encode($textAttrs).'|'.json_encode($symbols);
if (!isset($this->numberFormatters[$hash])) {
if (\count($this->numberFormatters) >= self::MAX_CACHED_FORMATTERS) {
array_shift($this->numberFormatters);
}
$this->numberFormatters[$hash] = new \NumberFormatter($locale, self::NUMBER_STYLES[$style]);
}
@@ -96,4 +96,35 @@ class IntlExtensionTest extends TestCase
$ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00'), 'short', 'short', 'yyyy-MM-dd HH:mm:ss', 'UTC', 'gregorian', 'en_US')
);
}
public function testDateFormatterCacheIsBounded()
{
$ext = new IntlExtension();
$env = new Environment(new ArrayLoader());
$date = new \DateTime('2020-02-20T13:37:00+00:00');
for ($i = 0; $i < 250; ++$i) {
$ext->formatDateTime($env, $date, 'medium', 'medium', 'yyyy-MM-dd-'.$i, 'UTC', 'gregorian', 'en_US');
}
$cache = (new \ReflectionProperty(IntlExtension::class, 'dateFormatters'))->getValue($ext);
$this->assertLessThanOrEqual(100, \count($cache));
$this->assertSame(
'2020-02-20-249',
$ext->formatDateTime($env, $date, 'medium', 'medium', 'yyyy-MM-dd-249', 'UTC', 'gregorian', 'en_US')
);
}
public function testNumberFormatterCacheIsBounded()
{
$ext = new IntlExtension();
for ($i = 0; $i < 250; ++$i) {
$ext->formatNumber(1, ['multiplier' => $i + 1], 'decimal', 'default', 'en_US');
}
$cache = (new \ReflectionProperty(IntlExtension::class, 'numberFormatters'))->getValue($ext);
$this->assertLessThanOrEqual(100, \count($cache));
$this->assertGreaterThan(1, \count($cache));
}
}