Modify XLSX RW to keep decimal for floats with a zero decimal part

Prior to 1.10, all numeric values where read as floats. In 1.10
numeric values are read using 0 + x, which relies on PHP type
juggling rules. As a result, float(0.0) is written as string('0'),
then read back as int(0). This fix causes the writer to retain the
the decimal for float values such that a reader can differentiate
floats from ints.

Closes #1262
This commit is contained in:
rtek 2019-11-25 11:00:15 -05:00 committed by Adrien Crivelli
parent 9552172b85
commit cf30c2a824
No known key found for this signature in database
GPG Key ID: B182FD79DC6DE92E
4 changed files with 62 additions and 9 deletions

View File

@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- FLOOR() function accept negative number and negative significance [#1245](https://github.com/PHPOffice/PhpSpreadsheet/pull/1245) - FLOOR() function accept negative number and negative significance [#1245](https://github.com/PHPOffice/PhpSpreadsheet/pull/1245)
- Correct column style even when using rowspan [#1249](https://github.com/PHPOffice/PhpSpreadsheet/pull/1249) - Correct column style even when using rowspan [#1249](https://github.com/PHPOffice/PhpSpreadsheet/pull/1249)
- XLSX reader/writer keep decimal for floats with a zero decimal part [#1262](https://github.com/PHPOffice/PhpSpreadsheet/pull/1262)
## [1.10.0] - 2019-11-18 ## [1.10.0] - 2019-11-18

View File

@ -729,15 +729,6 @@ class Xlsx extends BaseReader
// read empty cells or the cells are not empty // read empty cells or the cells are not empty
if ($this->readEmptyCells || ($value !== null && $value !== '')) { if ($this->readEmptyCells || ($value !== null && $value !== '')) {
// Check for numeric values
if (is_numeric($value) && $cellDataType != 's') {
if ($value == (int) $value) {
$value = (int) $value;
} elseif ($value == (float) $value) {
$value = (float) $value;
}
}
// Rich text? // Rich text?
if ($value instanceof RichText && $this->readDataOnly) { if ($value instanceof RichText && $this->readDataOnly) {
$value = $value->getPlainText(); $value = $value->getPlainText();

View File

@ -1135,6 +1135,13 @@ class Worksheet extends WriterPart
break; break;
case 'n': // Numeric case 'n': // Numeric
//force a decimal to be written if the type is float
if (is_float($cellValue)) {
$cellValue = (string) $cellValue;
if (strpos($cellValue, '.') === false) {
$cellValue = $cellValue . '.0';
}
}
// force point as decimal separator in case current locale uses comma // force point as decimal separator in case current locale uses comma
$objWriter->writeElement('v', str_replace(',', '.', $cellValue)); $objWriter->writeElement('v', str_replace(',', '.', $cellValue));

View File

@ -0,0 +1,54 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as Reader;
use PhpOffice\PhpSpreadsheet\Settings;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as Writer;
use PHPUnit\Framework\TestCase;
class FloatsRetainedTest extends TestCase
{
/**
* @dataProvider providerIntyFloatsRetainedByWriter
*
* @param float|int $value
*/
public function testIntyFloatsRetainedByWriter($value)
{
$outputFilename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
Settings::setLibXmlLoaderOptions(null);
$sheet = new Spreadsheet();
$sheet->getActiveSheet()->getCell('A1')->setValue($value);
$writer = new Writer($sheet);
$writer->save($outputFilename);
$reader = new Reader();
$sheet = $reader->load($outputFilename);
$this->assertSame($value, $sheet->getActiveSheet()->getCell('A1')->getValue());
}
public function providerIntyFloatsRetainedByWriter()
{
return [
[-1.0],
[-1],
[0.0],
[0],
[1.0],
[1],
[1e-3],
[1.3e-10],
[1e10],
[3.00000000000000000001],
[99999999999999999],
[99999999999999999.0],
[999999999999999999999999999999999999999999],
[999999999999999999999999999999999999999999.0],
];
}
}