mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-30 12:07:56 +00:00
e92b538efa
Fix #4776. DefaultValueBinder treats strings that look like scientific notation as floats. However, if Php evaluates them as `INF`, this can cause problems for Excel. A one-line change to DefaultValueBinder avoids this situation. Tests are added for DefaultValueBinder, and also for StringValueBinder and AdvancedValueBinder, although those extend DefaultValueBinder and needed no code changes.
278 lines
8.5 KiB
PHP
278 lines
8.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpOffice\PhpSpreadsheetTests\Cell;
|
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder;
|
|
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
|
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
|
|
use PhpOffice\PhpSpreadsheet\Settings;
|
|
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class AdvancedValueBinderTest extends TestCase
|
|
{
|
|
const AVB_PRECISION = 1.0E-8;
|
|
|
|
private string $originalLocale;
|
|
|
|
private IValueBinder $valueBinder;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->originalLocale = Settings::getLocale();
|
|
|
|
$this->valueBinder = Cell::getValueBinder();
|
|
Cell::setValueBinder(new AdvancedValueBinder());
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
StringHelper::setCurrencyCode(null);
|
|
StringHelper::setDecimalSeparator(null);
|
|
StringHelper::setThousandsSeparator(null);
|
|
Settings::setLocale($this->originalLocale);
|
|
Cell::setValueBinder($this->valueBinder);
|
|
}
|
|
|
|
public function testNullValue(): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue(null);
|
|
self::assertNull($sheet->getCell('A1')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public function testBoolean(): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue(true);
|
|
self::assertTrue($sheet->getCell('A1')->getValue());
|
|
|
|
$sheet->getCell('A2')->setValue(false);
|
|
self::assertFalse($sheet->getCell('A2')->getValue());
|
|
|
|
$sheet->getCell('A3')->setValue('true');
|
|
self::assertTrue($sheet->getCell('A3')->getValue());
|
|
|
|
$sheet->getCell('A4')->setValue('false');
|
|
self::assertFalse($sheet->getCell('A4')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public function testBooleanLocale(): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
Settings::setLocale('nl_NL');
|
|
|
|
$sheet->getCell('A1')->setValue('Waar');
|
|
self::assertTrue($sheet->getCell('A1')->getValue());
|
|
|
|
$sheet->getCell('A2')->setValue('OnWaar');
|
|
self::assertFalse($sheet->getCell('A2')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
#[DataProvider('currencyProvider')]
|
|
public function testCurrency(string $value, float $valueBinded, string $thousandsSeparator, string $decimalSeparator, string $currencyCode): void
|
|
{
|
|
StringHelper::setCurrencyCode($currencyCode);
|
|
StringHelper::setDecimalSeparator($decimalSeparator);
|
|
StringHelper::setThousandsSeparator($thousandsSeparator);
|
|
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function currencyProvider(): array
|
|
{
|
|
return [
|
|
['$10.11', 10.11, ',', '.', '$'],
|
|
['$1,010.12', 1010.12, ',', '.', '$'],
|
|
['$20,20', 20.2, '.', ',', '$'],
|
|
['$2.020,20', 2020.2, '.', ',', '$'],
|
|
['€2.020,20', 2020.2, '.', ',', '€'],
|
|
['€ 2.020,20', 2020.2, '.', ',', '€'],
|
|
['€2,020.22', 2020.22, ',', '.', '€'],
|
|
['$10.11', 10.11, ',', '.', '€'],
|
|
['€2,020.20', 2020.2, ',', '.', '$'],
|
|
'slash as group separator' => ['€2/020.20', 2020.2, '/', '.', '$'],
|
|
'slash as decimal separator' => ['€2,020/20', 2020.2, ',', '/', '$'],
|
|
['-2,020.20€', -2020.2, ',', '.', '$'],
|
|
['- 2,020.20 € ', -2020.2, ',', '.', '$'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('fractionProvider')]
|
|
public function testFractions(mixed $value, mixed $valueBinded): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function fractionProvider(): array
|
|
{
|
|
return [
|
|
['1/5', 0.2],
|
|
['-1/5', -0.2],
|
|
['- 1/5', -0.2],
|
|
['12/5', 2.4],
|
|
['2/100', 0.02],
|
|
['15/12', 1.25],
|
|
['20/100', 0.2],
|
|
['1 3/5', 1.6],
|
|
['-1 3/5', -1.6],
|
|
['1 4/20', 1.2],
|
|
['1 16/20', 1.8],
|
|
['12 20/100', 12.2],
|
|
['-1 4/20', -1.2],
|
|
['407 / ', '407 / '],
|
|
['407 /', '407 /'],
|
|
['407 3/', '407 3/'],
|
|
['-407 /4', -101.75],
|
|
[' /', ' /'],
|
|
[' / ', ' / '],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('percentageProvider')]
|
|
public function testPercentages(mixed $value, mixed $valueBinded): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function percentageProvider(): array
|
|
{
|
|
return [
|
|
['10%', 0.1],
|
|
['-12%', -0.12],
|
|
['120%', 1.2],
|
|
['12.5%', 0.125],
|
|
['-12.5%', -0.125],
|
|
['12,345%', 123.45],
|
|
['12,345.67%', 123.4567],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('timeProvider')]
|
|
public function testTimes(mixed $value, mixed $valueBinded): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertEqualsWithDelta($valueBinded, $sheet->getCell('A1')->getValue(), self::AVB_PRECISION);
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function timeProvider(): array
|
|
{
|
|
return [
|
|
['1:20', 0.05555555556],
|
|
['09:17', 0.386805555556],
|
|
['15:00', 0.625],
|
|
['17:12:35', 0.71707175926],
|
|
['23:58:20', 0.99884259259],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('stringProvider')]
|
|
public function testStringWrapping(string $value): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertEquals($value, $sheet->getCell('A1')->getValue());
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function stringProvider(): array
|
|
{
|
|
return [
|
|
['Hello World'],
|
|
["Hello\nWorld"],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('formulaProvider')]
|
|
public function testFormula(string $value, string $dataType): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertSame($dataType, $sheet->getCell('A1')->getDataType());
|
|
if ($dataType === DataType::TYPE_FORMULA) {
|
|
self::assertFalse($sheet->getStyle('A1')->getQuotePrefix());
|
|
} else {
|
|
self::assertTrue($sheet->getStyle('A1')->getQuotePrefix());
|
|
}
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function formulaProvider(): array
|
|
{
|
|
return [
|
|
'normal formula' => ['=SUM(A1:C3)', DataType::TYPE_FORMULA],
|
|
'issue 1310' => ['======', DataType::TYPE_STRING],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('nativeProvider')]
|
|
public function testNative(mixed $value, mixed $expected = null): void
|
|
{
|
|
$expected ??= $value;
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
$sheet->getCell('A1')->setValue($value);
|
|
self::assertSame($expected, $sheet->getCell('A1')->getValue());
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
public static function nativeProvider(): array
|
|
{
|
|
return [
|
|
'int' => [2],
|
|
'float' => [3.0],
|
|
'numeric string' => ['15', 15],
|
|
'scientific notation' => ['2.5E1', 25.0],
|
|
'issue4766 large exponent' => ['4E433'],
|
|
'issue4766 large negative exponent' => ['4E-433', 0.0],
|
|
'boolean' => [false],
|
|
'string' => ['xyz'],
|
|
];
|
|
}
|
|
}
|