Skip to content
This repository was archived by the owner on May 26, 2022. It is now read-only.

Commit 4a5da2a

Browse files
author
Adrien Loison
committed
Fix CellValueFormatter for numeric values
The value passed into the format() function is coming from an XML file and has never been coerced. Therefore, when checking is_int($value), the check always returns false - because it's a string. Changing the check fixes the issue and Spout now correctly parses large numbers.
1 parent c48c07d commit 4a5da2a

File tree

3 files changed

+67
-20
lines changed

3 files changed

+67
-20
lines changed

src/Spout/Reader/ODS/Helper/CellValueFormatter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ public function __construct()
4747

4848
/**
4949
* Returns the (unescaped) correctly marshalled, cell value associated to the given XML node.
50-
* @TODO Add other types !!
5150
* @see http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#refTable13
5251
*
5352
* @param \DOMNode $node
@@ -119,7 +118,8 @@ protected function formatStringCellValue($node)
119118
protected function formatFloatCellValue($node)
120119
{
121120
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
122-
$cellValue = is_int($nodeValue) ? intval($nodeValue) : floatval($nodeValue);
121+
$nodeIntValue = intval($nodeValue);
122+
$cellValue = ($nodeIntValue == $nodeValue) ? $nodeIntValue : floatval($nodeValue);
123123
return $cellValue;
124124
}
125125

src/Spout/Reader/XLSX/Helper/CellValueFormatter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ protected function formatNumericCellValue($nodeValue, $cellStyleId)
169169
if ($shouldFormatAsDate) {
170170
return $this->formatExcelTimestampValue(floatval($nodeValue));
171171
} else {
172-
return is_int($nodeValue) ? intval($nodeValue) : floatval($nodeValue);
172+
$nodeIntValue = intval($nodeValue);
173+
return ($nodeIntValue == $nodeValue) ? $nodeIntValue : floatval($nodeValue);
173174
}
174175
}
175176

tests/Spout/Reader/XLSX/Helper/CellValueFormatterTest.php

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,26 @@ class CellValueFormatterTest extends \PHPUnit_Framework_TestCase
1212
/**
1313
* @return array
1414
*/
15-
public function dataProviderForExcelDateTest()
15+
public function dataProviderForTestExcelDate()
1616
{
1717
return [
18-
[ CellValueFormatter::CELL_TYPE_NUMERIC, 42429, '2016-02-29 00:00:00' ],
19-
[ CellValueFormatter::CELL_TYPE_NUMERIC, '146098', '2299-12-31 00:00:00' ],
20-
[ CellValueFormatter::CELL_TYPE_NUMERIC, -700, null ],
21-
[ CellValueFormatter::CELL_TYPE_NUMERIC, 0, null ],
22-
[ CellValueFormatter::CELL_TYPE_NUMERIC, 0.5, null ],
23-
[ CellValueFormatter::CELL_TYPE_NUMERIC, 1, '1900-01-01 00:00:00' ],
24-
[ CellValueFormatter::CELL_TYPE_NUMERIC, 59.999988425926, '1900-02-28 23:59:59' ],
25-
[ CellValueFormatter::CELL_TYPE_NUMERIC, 60.458333333333, '1900-02-28 11:00:00' ],
18+
[CellValueFormatter::CELL_TYPE_NUMERIC, 42429, '2016-02-29 00:00:00'],
19+
[CellValueFormatter::CELL_TYPE_NUMERIC, '146098', '2299-12-31 00:00:00'],
20+
[CellValueFormatter::CELL_TYPE_NUMERIC, -700, null],
21+
[CellValueFormatter::CELL_TYPE_NUMERIC, 0, null],
22+
[CellValueFormatter::CELL_TYPE_NUMERIC, 0.5, null],
23+
[CellValueFormatter::CELL_TYPE_NUMERIC, 1, '1900-01-01 00:00:00'],
24+
[CellValueFormatter::CELL_TYPE_NUMERIC, 59.999988425926, '1900-02-28 23:59:59'],
25+
[CellValueFormatter::CELL_TYPE_NUMERIC, 60.458333333333, '1900-02-28 11:00:00'],
2626
];
2727
}
2828

2929
/**
30-
* @dataProvider dataProviderForExcelDateTest
30+
* @dataProvider dataProviderForTestExcelDate
3131
*
32+
* @param string $cellType
33+
* @param int|float|string $nodeValue
34+
* @param string|null $expectedDateAsString
3235
* @return void
3336
*/
3437
public function testExcelDate($cellType, $nodeValue, $expectedDateAsString)
@@ -39,16 +42,16 @@ public function testExcelDate($cellType, $nodeValue, $expectedDateAsString)
3942
->expects($this->atLeastOnce())
4043
->method('item')
4144
->with(0)
42-
->will($this->returnValue((object)[ 'nodeValue' => $nodeValue ]));
45+
->will($this->returnValue((object)['nodeValue' => $nodeValue]));
4346

4447
$nodeMock = $this->getMockBuilder('DOMElement')->disableOriginalConstructor()->getMock();
4548

4649
$nodeMock
4750
->expects($this->atLeastOnce())
4851
->method('getAttribute')
4952
->will($this->returnValueMap([
50-
[ CellValueFormatter::XML_ATTRIBUTE_TYPE, $cellType ],
51-
[ CellValueFormatter::XML_ATTRIBUTE_STYLE_ID, 123 ],
53+
[CellValueFormatter::XML_ATTRIBUTE_TYPE, $cellType],
54+
[CellValueFormatter::XML_ATTRIBUTE_STYLE_ID, 123],
5255
]));
5356

5457
$nodeMock
@@ -57,17 +60,16 @@ public function testExcelDate($cellType, $nodeValue, $expectedDateAsString)
5760
->with(CellValueFormatter::XML_NODE_VALUE)
5861
->will($this->returnValue($nodeListMock));
5962

60-
$styleHelperMock = $this->getMockBuilder(__NAMESPACE__ . '\StyleHelper')->disableOriginalConstructor()->getMock();
63+
$styleHelperMock = $this->getMockBuilder('Box\Spout\Reader\XLSX\Helper\StyleHelper')->disableOriginalConstructor()->getMock();
6164

6265
$styleHelperMock
6366
->expects($this->once())
6467
->method('shouldFormatNumericValueAsDate')
6568
->with(123)
6669
->will($this->returnValue(true));
6770

68-
$instance = new CellValueFormatter(null, $styleHelperMock);
69-
70-
$result = $instance->extractAndFormatNodeValue($nodeMock);
71+
$formatter = new CellValueFormatter(null, $styleHelperMock);
72+
$result = $formatter->extractAndFormatNodeValue($nodeMock);
7173

7274
if ($expectedDateAsString === null) {
7375
$this->assertNull($result);
@@ -77,4 +79,48 @@ public function testExcelDate($cellType, $nodeValue, $expectedDateAsString)
7779
}
7880
}
7981

82+
/**
83+
* @return array
84+
*/
85+
public function dataProviderForTestFormatNumericCellValueWithNumbers()
86+
{
87+
return [
88+
[42, 42, 'integer'],
89+
[42.5, 42.5, 'double'],
90+
[-42, -42, 'integer'],
91+
[-42.5, -42.5, 'double'],
92+
['42', 42, 'integer'],
93+
['42.5', 42.5, 'double'],
94+
[865640023012945, 865640023012945, 'integer'],
95+
['865640023012945', 865640023012945, 'integer'],
96+
[865640023012945.5, 865640023012945.5, 'double'],
97+
['865640023012945.5', 865640023012945.5, 'double'],
98+
[PHP_INT_MAX, PHP_INT_MAX, 'integer'],
99+
[~PHP_INT_MAX + 1, ~PHP_INT_MAX + 1, 'integer'], // ~PHP_INT_MAX === PHP_INT_MIN, PHP_INT_MIN being PHP7+
100+
[PHP_INT_MAX + 1, PHP_INT_MAX + 1, 'double'],
101+
];
102+
}
103+
104+
/**
105+
* @dataProvider dataProviderForTestFormatNumericCellValueWithNumbers
106+
*
107+
* @param int|float|string $value
108+
* @param int|float $expectedFormattedValue
109+
* @param string $expectedType
110+
* @return void
111+
*/
112+
public function testFormatNumericCellValueWithNumbers($value, $expectedFormattedValue, $expectedType)
113+
{
114+
$styleHelperMock = $this->getMockBuilder('Box\Spout\Reader\XLSX\Helper\StyleHelper')->disableOriginalConstructor()->getMock();
115+
$styleHelperMock
116+
->expects($this->once())
117+
->method('shouldFormatNumericValueAsDate')
118+
->will($this->returnValue(false));
119+
120+
$formatter = new CellValueFormatter(null, $styleHelperMock);
121+
$formattedValue = \ReflectionHelper::callMethodOnObject($formatter, 'formatNumericCellValue', $value, 0);
122+
123+
$this->assertEquals($expectedFormattedValue, $formattedValue);
124+
$this->assertEquals($expectedType, gettype($formattedValue));
125+
}
80126
}

0 commit comments

Comments
 (0)