Skip to content

Commit 687d87c

Browse files
authored
Merge pull request #4279 from oleibman/currsymbol
Unexpected Charset Possible for Currency Symbol
2 parents 1721aa4 + f00ebda commit 687d87c

File tree

5 files changed

+94
-38
lines changed

5 files changed

+94
-38
lines changed

.github/workflows/main.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ jobs:
2525
- name: Checkout
2626
uses: actions/checkout@v4
2727

28+
- name: Install locales
29+
run: sudo apt-get install -y language-pack-fr language-pack-de
30+
31+
- name: Install single-byte locale
32+
run: sudo sed -i -e 's/# de_DE@euro/de_DE@euro/g' /etc/locale.gen && sudo locale-gen de_DE@euro
33+
2834
- name: Setup PHP, with composer and extensions
2935
uses: shivammathur/setup-php@v2
3036
with:

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2525

2626
### Fixed
2727

28+
- More context options may be needed for http(s) image. [Php issue 17121](https://github.com/php/php-src/issues/17121) [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
29+
- Avoid unexpected charset in currency symbol. [PR #4279](https://github.com/PHPOffice/PhpSpreadsheet/pull/4279)
2830
- Add forceFullCalc option to Xlsx Writer. [Issue #4269](https://github.com/PHPOffice/PhpSpreadsheet/issues/4269) [PR #4271](https://github.com/PHPOffice/PhpSpreadsheet/pull/4271)
2931
- More context options may be needed for http(s) image. [Php issue 17121](https://github.com/php/php-src/issues/17121) [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
3032
- Coverage-related tweaks to Xls Reader. [PR #4277](https://github.com/PHPOffice/PhpSpreadsheet/pull/4277)

src/PhpSpreadsheet/Shared/StringHelper.php

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ class StringHelper
1919
/**
2020
* Decimal separator.
2121
*/
22-
private static ?string $decimalSeparator;
22+
private static ?string $decimalSeparator = null;
2323

2424
/**
2525
* Thousands separator.
2626
*/
27-
private static ?string $thousandsSeparator;
27+
private static ?string $thousandsSeparator = null;
2828

2929
/**
3030
* Currency code.
3131
*/
32-
private static ?string $currencyCode;
32+
private static ?string $currencyCode = null;
3333

3434
/**
3535
* Is iconv extension avalable?
@@ -511,21 +511,31 @@ public static function strCaseReverse(string $textValue): string
511511
return implode('', $characters);
512512
}
513513

514+
private static function useAlt(string $altValue, string $default, bool $trimAlt): string
515+
{
516+
return ($trimAlt ? trim($altValue) : $altValue) ?: $default;
517+
}
518+
519+
private static function getLocaleValue(string $key, string $altKey, string $default, bool $trimAlt = false): string
520+
{
521+
$localeconv = localeconv();
522+
$rslt = $localeconv[$key];
523+
// win-1252 implements Euro as 0x80 plus other symbols
524+
if (preg_match('//u', $rslt) !== 1) {
525+
$rslt = '';
526+
}
527+
528+
return $rslt ?: self::useAlt($localeconv[$altKey], $default, $trimAlt);
529+
}
530+
514531
/**
515532
* Get the decimal separator. If it has not yet been set explicitly, try to obtain number
516533
* formatting information from locale.
517534
*/
518535
public static function getDecimalSeparator(): string
519536
{
520537
if (!isset(self::$decimalSeparator)) {
521-
$localeconv = localeconv();
522-
self::$decimalSeparator = ($localeconv['decimal_point'] != '')
523-
? $localeconv['decimal_point'] : $localeconv['mon_decimal_point'];
524-
525-
if (self::$decimalSeparator == '') {
526-
// Default to .
527-
self::$decimalSeparator = '.';
528-
}
538+
self::$decimalSeparator = self::getLocaleValue('decimal_point', 'mon_decimal_point', '.');
529539
}
530540

531541
return self::$decimalSeparator;
@@ -549,14 +559,7 @@ public static function setDecimalSeparator(?string $separator): void
549559
public static function getThousandsSeparator(): string
550560
{
551561
if (!isset(self::$thousandsSeparator)) {
552-
$localeconv = localeconv();
553-
self::$thousandsSeparator = ($localeconv['thousands_sep'] != '')
554-
? $localeconv['thousands_sep'] : $localeconv['mon_thousands_sep'];
555-
556-
if (self::$thousandsSeparator == '') {
557-
// Default to .
558-
self::$thousandsSeparator = ',';
559-
}
562+
self::$thousandsSeparator = self::getLocaleValue('thousands_sep', 'mon_thousands_sep', ',');
560563
}
561564

562565
return self::$thousandsSeparator;
@@ -577,22 +580,10 @@ public static function setThousandsSeparator(?string $separator): void
577580
* Get the currency code. If it has not yet been set explicitly, try to obtain the
578581
* symbol information from locale.
579582
*/
580-
public static function getCurrencyCode(): string
583+
public static function getCurrencyCode(bool $trimAlt = false): string
581584
{
582-
if (!empty(self::$currencyCode)) {
583-
return self::$currencyCode;
584-
}
585-
self::$currencyCode = '$';
586-
$localeconv = localeconv();
587-
if (!empty($localeconv['currency_symbol'])) {
588-
self::$currencyCode = $localeconv['currency_symbol'];
589-
590-
return self::$currencyCode;
591-
}
592-
if (!empty($localeconv['int_curr_symbol'])) {
593-
self::$currencyCode = $localeconv['int_curr_symbol'];
594-
595-
return self::$currencyCode;
585+
if (!isset(self::$currencyCode)) {
586+
self::$currencyCode = self::getLocaleValue('currency_symbol', 'int_curr_symbol', '$', $trimAlt);
596587
}
597588

598589
return self::$currencyCode;

tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatLocaleTest.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66

77
use PhpOffice\PhpSpreadsheet\Cell\DataType;
88
use PhpOffice\PhpSpreadsheet\Reader\Csv;
9+
use PHPUnit\Framework\Attributes;
10+
use PHPUnit\Framework\Attributes\DataProvider;
911
use PHPUnit\Framework\TestCase;
1012

13+
// separate processes due to setLocale
14+
#[Attributes\RunTestsInSeparateProcesses]
1115
class CsvNumberFormatLocaleTest extends TestCase
1216
{
1317
private bool $localeAdjusted;
@@ -44,8 +48,7 @@ protected function tearDown(): void
4448
}
4549
}
4650

47-
#[\PHPUnit\Framework\Attributes\DataProvider('providerNumberFormatNoConversionTest')]
48-
#[\PHPUnit\Framework\Attributes\RunInSeparateProcess]
51+
#[DataProvider('providerNumberFormatNoConversionTest')]
4952
public function testNumberFormatNoConversion(mixed $expectedValue, string $expectedFormat, string $cellAddress): void
5053
{
5154
if (!$this->localeAdjusted) {
@@ -85,8 +88,7 @@ public static function providerNumberFormatNoConversionTest(): array
8588
];
8689
}
8790

88-
#[\PHPUnit\Framework\Attributes\DataProvider('providerNumberValueConversionTest')]
89-
#[\PHPUnit\Framework\Attributes\RunInSeparateProcess]
91+
#[DataProvider('providerNumberValueConversionTest')]
9092
public function testNumberValueConversion(mixed $expectedValue, string $cellAddress): void
9193
{
9294
if (!$this->localeAdjusted) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Shared;
6+
7+
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
8+
use PHPUnit\Framework\Attributes;
9+
use PHPUnit\Framework\TestCase;
10+
11+
// separate processes due to setLocale
12+
#[Attributes\RunTestsInSeparateProcesses]
13+
class StringHelperLocaleTest extends TestCase
14+
{
15+
/**
16+
* @var false|string
17+
*/
18+
private $currentLocale;
19+
20+
protected function setUp(): void
21+
{
22+
$this->currentLocale = setlocale(LC_ALL, '0');
23+
}
24+
25+
protected function tearDown(): void
26+
{
27+
if (is_string($this->currentLocale)) {
28+
setlocale(LC_ALL, $this->currentLocale);
29+
}
30+
StringHelper::setCurrencyCode(null);
31+
}
32+
33+
public function testCurrency(): void
34+
{
35+
if ($this->currentLocale === false || !setlocale(LC_ALL, 'de_DE.UTF-8', 'deu_deu.utf8')) {
36+
self::markTestSkipped('Unable to set German UTF8 locale for testing.');
37+
}
38+
$result = StringHelper::getCurrencyCode();
39+
self::assertSame('', $result);
40+
if (!setlocale(LC_ALL, $this->currentLocale)) {
41+
self::markTestSkipped('Unable to restore default locale.');
42+
}
43+
$result = StringHelper::getCurrencyCode();
44+
self::assertSame('', $result, 'result persists despite locale change');
45+
StringHelper::setCurrencyCode(null);
46+
$result = StringHelper::getCurrencyCode();
47+
self::assertSame('$', $result, 'locale now used');
48+
StringHelper::setCurrencyCode(null);
49+
if (!setlocale(LC_ALL, 'deu_deu', 'de_DE@euro')) {
50+
self::markTestSkipped('Unable to set German single-byte locale for testing.');
51+
}
52+
$result = StringHelper::getCurrencyCode(true); // trim if alt symbol is used
53+
self::assertSame('EUR', $result, 'non-UTF8 result ignored');
54+
}
55+
}

0 commit comments

Comments
 (0)