Files
oleibman 31174c1c90 Set All Locale Variables at Once in a Threadsafe Manner
Fix #954, which went stale 6 years ago and which I have now reopened. The `setLocale` method in `Calculation` and `Settings` does not affect the 3 localizable properties in `StringHelper` - `currencyCode`, `decimalSeparator`, and `thousandsSeparator`. One way to work around this problem is to set those properties separately; this is probably how most people handle it. Another way is to call Php's own `setlocale` function; this does not require any foreknowledge of what the values need to be, but it comes with baggage (data is maintained at process level rather than thread level), so its use is discouraged.

This PR adds a new `setLocale` method to `StringHelper`. It sets the 3 properties and the `Calculation` language all at once. It depends on the `Intl` extension, which is a recommendation but not a formal requirement for PhpSpreadsheet; the method will return `false` if `Intl` is not available or it thinks the supplied locale is not valid.
2025-12-25 19:24:44 -08:00

161 lines
5.9 KiB
PHP

<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Shared;
use DateTime;
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
use PHPUnit\Framework\TestCase;
class StringHelperTest extends TestCase
{
protected function tearDown(): void
{
StringHelper::setLocale(null);
}
public function testGetIsIconvEnabled(): void
{
$result = StringHelper::getIsIconvEnabled();
self::assertTrue($result);
}
public function testGetDecimalSeparator(): void
{
$localeconv = localeconv();
$expectedResult = (!empty($localeconv['decimal_point'])) ? $localeconv['decimal_point'] : ',';
$result = StringHelper::getDecimalSeparator();
self::assertEquals($expectedResult, $result);
}
public function testSetDecimalSeparator(): void
{
$expectedResult = ',';
StringHelper::setDecimalSeparator($expectedResult);
$result = StringHelper::getDecimalSeparator();
self::assertEquals($expectedResult, $result);
}
public function testGetThousandsSeparator(): void
{
$localeconv = localeconv();
$expectedResult = (!empty($localeconv['thousands_sep'])) ? $localeconv['thousands_sep'] : ',';
$result = StringHelper::getThousandsSeparator();
self::assertEquals($expectedResult, $result);
}
public function testSetThousandsSeparator(): void
{
$expectedResult = ' ';
StringHelper::setThousandsSeparator($expectedResult);
$result = StringHelper::getThousandsSeparator();
self::assertEquals($expectedResult, $result);
}
public function testGetCurrencyCode(): void
{
$localeconv = localeconv();
$expectedResult = (!empty($localeconv['currency_symbol']) ? $localeconv['currency_symbol'] : (!empty($localeconv['int_curr_symbol']) ? $localeconv['int_curr_symbol'] : '$'));
$result = StringHelper::getCurrencyCode();
self::assertEquals($expectedResult, $result);
}
public function testSetCurrencyCode(): void
{
$expectedResult = '£';
StringHelper::setCurrencyCode($expectedResult);
$result = StringHelper::getCurrencyCode();
self::assertEquals($expectedResult, $result);
}
public function testControlCharacterPHP2OOXML(): void
{
$expectedResult = 'foo_x000B_bar';
$result = StringHelper::controlCharacterPHP2OOXML('foo' . chr(11) . 'bar');
self::assertEquals($expectedResult, $result);
}
public function testControlCharacterOOXML2PHP(): void
{
$expectedResult = 'foo' . chr(11) . 'bar';
$result = StringHelper::controlCharacterOOXML2PHP('foo_x000B_bar');
self::assertEquals($expectedResult, $result);
}
public function testSYLKtoUTF8(): void
{
$expectedResult = 'foo' . chr(11) . 'bar';
$result = StringHelper::SYLKtoUTF8("foo\x1B ;bar");
self::assertEquals($expectedResult, $result);
}
public function testIssue3900(): void
{
StringHelper::setDecimalSeparator('.');
StringHelper::setThousandsSeparator('');
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', 1.4);
$sheet->setCellValue('B1', 1004.5);
$sheet->setCellValue('C1', 1000000.5);
ob_start();
$ioWriter = new Csv($spreadsheet);
$ioWriter->setDelimiter(';');
$ioWriter->save('php://output');
$output = ob_get_clean();
$spreadsheet->disconnectWorksheets();
self::assertSame('"1.4";"1004.5";"1000000.5"' . PHP_EOL, $output);
}
public function testNonStringable(): void
{
$dt = new DateTime();
self::assertSame('', StringHelper::convertToString($dt, false));
$this->expectException(SpreadsheetException::class);
$this->expectExceptionMessage('Unable to convert to string');
StringHelper::convertToString($dt);
}
public function testNoIconv(): void
{
$string = "\xBF"; // inverted question mark in ISO-8859-1
$expected = '¿';
$from = 'ISO-8859-1';
$to = 'UTF-8';
self::assertSame($expected, StringHelper::convertEncoding($string, $to, $from));
self::assertSame('EUR', StringHelper::convertEncoding('€', 'ISO-8859-1', 'UTF-8'), 'transliterated');
self::assertSame($expected, StringHelperNoIconv::convertEncoding($string, $to, $from));
self::assertSame('?', StringHelperNoIconv::convertEncoding('€', 'ISO-8859-1', 'UTF-8'), 'using MB so no transliteration - just return question mark');
self::assertSame($expected, StringHelperNoIconv2::convertEncoding($string, $to, $from));
self::assertSame('?', StringHelperNoIconv2::convertEncoding('€', 'ISO-8859-1', 'UTF-8'), 'using MB so no transliteration - just return question mark');
self::assertSame($expected, StringHelperNoIconv3::convertEncoding($string, $to, $from));
self::assertSame('?', StringHelperNoIconv3::convertEncoding('€', 'ISO-8859-1', 'UTF-8'), 'using MB so no transliteration - just return question mark');
$noTransliteration = 'ツ';
// There is inconsistency in how //TRANSLIT operates.
// On some systems, '?' seems to be used if there is
// no other transliteration available.
$ignoreResult = '?';
if (StringHelper::getIsIconvEnabled()) {
$ignoreResult = iconv('UTF-8', 'ISO-8859-1//IGNORE//TRANSLIT', $noTransliteration);
self::assertContains($ignoreResult, ['', '?']);
}
self::assertSame($ignoreResult, StringHelper::convertEncoding($noTransliteration, 'ISO-8859-1', 'UTF-8'), 'no transliteration available');
self::assertSame('', StringHelper::convertEncoding($noTransliteration, 'ISO-8859-1', 'UTF-8', '//IGNORE'), 'TRANSLIT not used');
}
}