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

Commit fde8a49

Browse files
committed
Inline strings can contain multiple value nodes
We were working under the assumption that XLSX's inline strings only had a single value node (`<t>`). This is incorrect. To get the actual value of an inline string node, we need to concatenate the value of all its child nodes.
1 parent 69eeeff commit fde8a49

File tree

5 files changed

+30
-7
lines changed

5 files changed

+30
-7
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"require": {
1515
"php": ">=7.2.0",
1616
"ext-zip": "*",
17-
"ext-xmlreader" : "*"
17+
"ext-xmlreader": "*",
18+
"ext-dom": "*"
1819
},
1920
"require-dev": {
2021
"phpunit/phpunit": "^8",

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,15 @@ protected function getVNodeValue($node)
122122
*/
123123
protected function formatInlineStringCellValue($node)
124124
{
125-
// inline strings are formatted this way:
126-
// <c r="A1" t="inlineStr"><is><t>[INLINE_STRING]</t></is></c>
127-
$tNode = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE)->item(0);
128-
$cellValue = $this->escaper->unescape($tNode->nodeValue);
125+
// inline strings are formatted this way (they can contain any number of <t> nodes):
126+
// <c r="A1" t="inlineStr"><is><t>[INLINE_STRING]</t><t>[INLINE_STRING_2]</t></is></c>
127+
$tNodes = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE);
128+
129+
$cellValue = '';
130+
for ($i = 0; $i < $tNodes->count(); $i++) {
131+
$tNode = $tNodes->item($i);
132+
$cellValue .= $this->escaper->unescape($tNode->nodeValue);
133+
}
129134

130135
return $cellValue;
131136
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,22 @@ public function dataProviderForTestFormatStringCellValue()
186186
public function testFormatInlineStringCellValue($value, $expectedFormattedValue)
187187
{
188188
$nodeListMock = $this->createMock(\DOMNodeList::class);
189+
$nodeListMock
190+
->expects($this->atLeastOnce())
191+
->method('count')
192+
->willReturn(1);
189193
$nodeListMock
190194
->expects($this->atLeastOnce())
191195
->method('item')
192196
->with(0)
193-
->will($this->returnValue((object) ['nodeValue' => $value]));
197+
->willReturn((object) ['nodeValue' => $value]);
194198

195199
$nodeMock = $this->createMock(\DOMElement::class);
196200
$nodeMock
197201
->expects($this->atLeastOnce())
198202
->method('getElementsByTagName')
199203
->with(CellValueFormatter::XML_NODE_INLINE_STRING_VALUE)
200-
->will($this->returnValue($nodeListMock));
204+
->willReturn($nodeListMock);
201205

202206
$formatter = new CellValueFormatter(null, null, false, false, new Escaper\XLSX());
203207
$formattedValue = \ReflectionHelper::callMethodOnObject($formatter, 'formatInlineStringCellValue', $nodeMock);

tests/Spout/Reader/XLSX/ReaderTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ public function testReadForAllWorksheets($resourceName, $expectedNumOfRows, $exp
7272
}
7373
}
7474

75+
/**
76+
* @return void
77+
*/
78+
public function testReadShouldSupportInlineStringsWithMultipleValueNodes()
79+
{
80+
$allRows = $this->getAllRowsForFile('sheet_with_multiple_value_nodes_in_inline_strings.xlsx');
81+
82+
$expectedRows = [
83+
['VALUE 1 VALUE 2 VALUE 3 VALUE 4', 's1 - B1'],
84+
];
85+
$this->assertEquals($expectedRows, $allRows);
86+
}
87+
7588
/**
7689
* @return void
7790
*/
Binary file not shown.

0 commit comments

Comments
 (0)