Skip to content

Commit 4611e41

Browse files
committed
[BUGFIX] Ensure correct formatting in PHP 8.4
Since php 8.4 the ext-intl class `NumberFormatter` expects a strict locale string and throws a `ValueError` if the given value cannot get parsed. Together with the `uc` of TYPO3, which holds the backend language for the currently logged-in user, this could be set to `default`, which is no valid locale. New TYPO3 instances will write a `uc['lang']=''`, if the person uses `englisch`, which is the default, but older used to write `us['lang']='default'`, which is still correct. This throws the `ValueError` if the NumberFormatter should format with this setting. The Locales usage is not used here (other than on #491), as TYPO3 11 does not provide the necessary functions here. Fixes #486
1 parent e302191 commit 4611e41

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

Classes/Service/UsageService.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,15 @@ public function formatNumber(int $number)
7070
// locale en or null, so we call with default parameter.
7171
$numberFormatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL);
7272
} else {
73-
$language = 'en';
73+
$language = 'default';
7474
if ($this->getBackendUser() !== null) {
7575
$uc = $this->getBackendUser()->uc;
7676
if (is_array($uc) && array_key_exists('lang', $uc)) {
7777
$language = $uc['lang'];
7878
}
7979
}
80-
$numberFormatter = new \NumberFormatter($language, \NumberFormatter::DECIMAL);
80+
$locale = ($language !== 'default') ? $language : 'en';
81+
$numberFormatter = new \NumberFormatter($locale, \NumberFormatter::DECIMAL);
8182
}
8283
return $numberFormatter->format($number);
8384
}

Tests/Functional/Services/Fixtures/Pages.csv

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ pages,,,,,
55
tt_content,
66
,uid,pid,header,CType,bodytext
77
,3,1,"DeepL-Functional-Test Element","text",""
8+
"be_users"
9+
,"uid","pid","tstamp","username","password","admin","disable","starttime","endtime","options","crdate","workspace_perms","deleted","TSconfig","lastlogin","workspace_id","uc"
10+
# The password is "password"
11+
# The user has a broken/outdated uc lang configuration. This is required testing the behaviour of #486
12+
,1,0,1366642540,"admin","$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1",1,0,0,0,0,1366642540,1,0,,1371033743,0,"a:19:{s:10:""moduleData"";a:7:{s:28:""dashboard/current_dashboard/"";s:40:""764f4d1012c7b870a98ffd1e8dd3ff5e133cb451"";s:9:""scheduler"";a:1:{s:6:""action"";s:16:""scheduler_manage"";}s:13:""system_config"";a:1:{s:4:""tree"";s:3:""tca"";}s:10:""web_layout"";a:3:{s:8:""function"";s:1:""2"";s:8:""language"";s:1:""2"";s:10:""showHidden"";b:1;}s:10:""FormEngine"";a:2:{i:0;a:2:{s:32:""c31c3d00814edbf9b2ddab640af3f55d"";a:5:{i:0;s:116:""Bacon ipsum dolor sit strong amet capicola jerky pork chop rump shoulder shank. Shankle strip steak pig salami link."";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:14;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B14%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:14;s:3:""pid"";i:10;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:99:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=10#element-tt_content-14"";}s:32:""c312013d83c1a6ad7fec8b36a37ba3c8"";a:5:{i:0;s:25:""TYPO3 Styleguide Frontend"";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:1;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:33:""&edit%5Btt_content%5D%5B1%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:1;s:3:""pid"";i:1;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:98:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=1&#element-tt_content-1"";}}i:1;s:32:""d63be24a7702dd6c8c0504bfe838a532"";}s:57:""TYPO3\CMS\Backend\Utility\BackendUtility::getUpdateSignal"";a:0:{}s:16:""opendocs::recent"";a:5:{s:32:""d63be24a7702dd6c8c0504bfe838a532"";a:5:{i:0;s:120:""Bacon ipsum dolor sit strong amet capicola jerky pork chop rump shoulder shank. Haxe Streifen Steak Schwein Salami Link."";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:44;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B44%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:44;s:3:""pid"";i:8;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:99:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=8&#element-tt_content-44"";}s:32:""ffbd6ae78a9aa555f88d6295c30fb80c"";a:5:{i:0;s:116:""Bacon ipsum dolor sit strong amet capicola jerky pork chop rump shoulder shank. Shankle strip steak pig salami link."";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:10;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B10%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:10;s:3:""pid"";i:8;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:98:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=8#element-tt_content-10"";}s:32:""f3a80bc04cdfd6ed305676c6deecde13"";a:5:{i:0;s:25:""TYPO3 Styleguide Frontend"";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:43;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B43%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:43;s:3:""pid"";i:1;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:70:""/typo3/module/dashboard?token=dcec9d55204841dc643c2622b1dd11306044d7fa"";}s:32:""c312013d83c1a6ad7fec8b36a37ba3c8"";a:5:{i:0;s:25:""TYPO3 Styleguide Frontend"";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:1;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:33:""&edit%5Btt_content%5D%5B1%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:1;s:3:""pid"";i:1;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:97:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=1#element-tt_content-1"";}s:32:""9b967901d6c9df7fbe10e9cd1eacc0fe"";a:5:{i:0;s:24:""styleguide frontend demo"";i:1;a:5:{s:4:""edit"";a:1:{s:5:""pages"";a:1:{i:1;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";a:1:{s:5:""pages"";a:1:{s:16:""sys_language_uid"";s:1:""0"";}}s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:76:""&edit%5Bpages%5D%5B1%5D=edit&overrideVals%5Bpages%5D%5Bsys_language_uid%5D=0"";i:3;a:5:{s:5:""table"";s:5:""pages"";s:3:""uid"";i:1;s:3:""pid"";i:0;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:217:""/typo3/record/edit?token=ae076486b94686606614f90d8a155ec1f785183e&edit%5Btt_content%5D%5B14%5D=edit&returnUrl=/typo3/module/web/layout?token%3D2fb2757ce2a0f7275162273a7363c3cdf5ae31e0%26id%3D10%23element-tt_content-14"";}}}s:14:""emailMeAtLogin"";i:0;s:8:""titleLen"";s:2:""50"";s:20:""edit_docModuleUpload"";i:1;s:15:""moduleSessionID"";a:7:{s:28:""dashboard/current_dashboard/"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:9:""scheduler"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:13:""system_config"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:10:""web_layout"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:10:""FormEngine"";s:40:""ac1694175250c17ad2c05ef590e41376512745be"";s:57:""TYPO3\CMS\Backend\Utility\BackendUtility::getUpdateSignal"";s:40:""75d90be2aa2bf0625bafeee69c3fdd82f726db12"";s:16:""opendocs::recent"";s:40:""ac1694175250c17ad2c05ef590e41376512745be"";}s:8:""realName"";s:0:"""";s:5:""email"";s:0:"""";s:8:""password"";s:0:"""";s:9:""password2"";s:0:"""";s:6:""avatar"";s:0:"""";s:4:""lang"";s:7:""default"";s:11:""startModule"";s:0:"""";s:25:""showHiddenFilesAndFolders"";i:0;s:10:""copyLevels"";s:0:"""";s:18:""resetConfiguration"";s:0:"""";s:12:""mfaProviders"";s:0:"""";s:18:""backendTitleFormat"";s:10:""titleFirst"";s:11:""colorScheme"";s:4:""auto"";s:5:""theme"";s:6:""modern"";}"
13+
,2,0,1366642540,"admin2","$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1",1,0,0,0,0,1366642540,1,0,,1371033743,0,"a:19:{s:10:""moduleData"";a:7:{s:28:""dashboard/current_dashboard/"";s:40:""764f4d1012c7b870a98ffd1e8dd3ff5e133cb451"";s:9:""scheduler"";a:1:{s:6:""action"";s:16:""scheduler_manage"";}s:13:""system_config"";a:1:{s:4:""tree"";s:3:""tca"";}s:10:""web_layout"";a:3:{s:8:""function"";s:1:""2"";s:8:""language"";s:1:""2"";s:10:""showHidden"";b:1;}s:10:""FormEngine"";a:2:{i:0;a:2:{s:32:""c31c3d00814edbf9b2ddab640af3f55d"";a:5:{i:0;s:116:""Bacon ipsum dolor sit strong amet capicola jerky pork chop rump shoulder shank. Shankle strip steak pig salami link."";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:14;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B14%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:14;s:3:""pid"";i:10;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:99:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=10#element-tt_content-14"";}s:32:""c312013d83c1a6ad7fec8b36a37ba3c8"";a:5:{i:0;s:25:""TYPO3 Styleguide Frontend"";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:1;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:33:""&edit%5Btt_content%5D%5B1%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:1;s:3:""pid"";i:1;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:98:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=1&#element-tt_content-1"";}}i:1;s:32:""d63be24a7702dd6c8c0504bfe838a532"";}s:57:""TYPO3\CMS\Backend\Utility\BackendUtility::getUpdateSignal"";a:0:{}s:16:""opendocs::recent"";a:5:{s:32:""d63be24a7702dd6c8c0504bfe838a532"";a:5:{i:0;s:120:""Bacon ipsum dolor sit strong amet capicola jerky pork chop rump shoulder shank. Haxe Streifen Steak Schwein Salami Link."";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:44;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B44%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:44;s:3:""pid"";i:8;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:99:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=8&#element-tt_content-44"";}s:32:""ffbd6ae78a9aa555f88d6295c30fb80c"";a:5:{i:0;s:116:""Bacon ipsum dolor sit strong amet capicola jerky pork chop rump shoulder shank. Shankle strip steak pig salami link."";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:10;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B10%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:10;s:3:""pid"";i:8;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:98:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=8#element-tt_content-10"";}s:32:""f3a80bc04cdfd6ed305676c6deecde13"";a:5:{i:0;s:25:""TYPO3 Styleguide Frontend"";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:43;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:34:""&edit%5Btt_content%5D%5B43%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:43;s:3:""pid"";i:1;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:70:""/typo3/module/dashboard?token=dcec9d55204841dc643c2622b1dd11306044d7fa"";}s:32:""c312013d83c1a6ad7fec8b36a37ba3c8"";a:5:{i:0;s:25:""TYPO3 Styleguide Frontend"";i:1;a:5:{s:4:""edit"";a:1:{s:10:""tt_content"";a:1:{i:1;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";N;s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:33:""&edit%5Btt_content%5D%5B1%5D=edit"";i:3;a:5:{s:5:""table"";s:10:""tt_content"";s:3:""uid"";i:1;s:3:""pid"";i:1;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:97:""/typo3/module/web/layout?token=2fb2757ce2a0f7275162273a7363c3cdf5ae31e0&id=1#element-tt_content-1"";}s:32:""9b967901d6c9df7fbe10e9cd1eacc0fe"";a:5:{i:0;s:24:""styleguide frontend demo"";i:1;a:5:{s:4:""edit"";a:1:{s:5:""pages"";a:1:{i:1;s:4:""edit"";}}s:7:""defVals"";N;s:12:""overrideVals"";a:1:{s:5:""pages"";a:1:{s:16:""sys_language_uid"";s:1:""0"";}}s:11:""columnsOnly"";N;s:6:""noView"";N;}i:2;s:76:""&edit%5Bpages%5D%5B1%5D=edit&overrideVals%5Bpages%5D%5Bsys_language_uid%5D=0"";i:3;a:5:{s:5:""table"";s:5:""pages"";s:3:""uid"";i:1;s:3:""pid"";i:0;s:3:""cmd"";s:4:""edit"";s:12:""deleteAccess"";b:1;}i:4;s:217:""/typo3/record/edit?token=ae076486b94686606614f90d8a155ec1f785183e&edit%5Btt_content%5D%5B14%5D=edit&returnUrl=/typo3/module/web/layout?token%3D2fb2757ce2a0f7275162273a7363c3cdf5ae31e0%26id%3D10%23element-tt_content-14"";}}}s:14:""emailMeAtLogin"";i:0;s:8:""titleLen"";s:2:""50"";s:20:""edit_docModuleUpload"";i:1;s:15:""moduleSessionID"";a:7:{s:28:""dashboard/current_dashboard/"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:9:""scheduler"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:13:""system_config"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:10:""web_layout"";s:40:""e47ba98e40d443d825f5d08a6545e37bbc86bac5"";s:10:""FormEngine"";s:40:""ac1694175250c17ad2c05ef590e41376512745be"";s:57:""TYPO3\CMS\Backend\Utility\BackendUtility::getUpdateSignal"";s:40:""75d90be2aa2bf0625bafeee69c3fdd82f726db12"";s:16:""opendocs::recent"";s:40:""ac1694175250c17ad2c05ef590e41376512745be"";}s:8:""realName"";s:0:"""";s:5:""email"";s:0:"""";s:8:""password"";s:0:"""";s:9:""password2"";s:0:"""";s:6:""avatar"";s:0:"""";s:4:""lang"";s:2:""de"";s:11:""startModule"";s:0:"""";s:25:""showHiddenFilesAndFolders"";i:0;s:10:""copyLevels"";s:0:"""";s:18:""resetConfiguration"";s:0:"""";s:12:""mfaProviders"";s:0:"""";s:18:""backendTitleFormat"";s:10:""titleFirst"";s:11:""colorScheme"";s:4:""auto"";s:5:""theme"";s:6:""modern"";}"

Tests/Functional/Services/UsageServiceTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use DeepL\Usage;
88
use DeepL\UsageDetail;
9+
use TYPO3\CMS\Core\Information\Typo3Version;
910
use WebVision\WvDeepltranslate\Service\DeeplService;
1011
use WebVision\WvDeepltranslate\Service\ProcessingInstruction;
1112
use WebVision\WvDeepltranslate\Service\UsageService;
@@ -113,4 +114,44 @@ public function checkHTMLMarkupsIsNotPartOfLimit(): void
113114
static::assertInstanceOf(UsageDetail::class, $character);
114115
static::assertEquals(strlen($translateContent), $character->count);
115116
}
117+
118+
public static function numberFormatterLocalesDataProvider(): \Generator
119+
{
120+
// yield 'Default formats to english' => [
121+
// 'user' => 1,
122+
// 'number' => 20000,
123+
// 'expectedFormat' => '20,000',
124+
// ];
125+
yield 'BE uc lang "de" formats german' => [
126+
'user' => 2,
127+
'number' => 93254850,
128+
'expectedFormat' => '93.254.850',
129+
];
130+
}
131+
/**
132+
* This test ensures that in PHP >=8.4 the NumberFormatter works correctly.
133+
* With migrated TYPO3 data there is the possibility that uc['lang'] is set to 'default',
134+
* which is no correct format for a locale the number formatter accepts. THis will lead
135+
* to an error during initialisation.
136+
*
137+
* @test
138+
* @dataProvider numberFormatterLocalesDataProvider
139+
*/
140+
public function numberFormatRespectsLocalesAndDefault(
141+
int $user,
142+
int $number,
143+
string $expectedFormat
144+
): void {
145+
// TYPO3 v11 seems not to respect the UC language setting, if other than default. skip this test for user 2
146+
if ((new Typo3Version())->getMajorVersion() <= 11 && $user === 2) {
147+
static::markTestSkipped('The issue only appears in PHP 8.4, which is not supported with TYPO3 11');
148+
}
149+
$this->importCSVDataSet(__DIR__ . '/Fixtures/Pages.csv');
150+
$this->setUpBackendUser($user);
151+
/** @var UsageService $usageService */
152+
$usageService = $this->get(UsageService::class);
153+
154+
$formatted = $usageService->formatNumber($number);
155+
static::assertEquals($expectedFormat, $formatted);
156+
}
116157
}

0 commit comments

Comments
 (0)