From 0b5d84831177ce07e8f76ba56eab2820f34b9625 Mon Sep 17 00:00:00 2001 From: Adrien Loison Date: Mon, 11 Jul 2016 16:38:41 +0200 Subject: [PATCH] Add support for missing cell reference When describing a cell, the cell reference (r="A1") is optional. When not present, we should just increment the index of the last processed row. --- src/Spout/Reader/XLSX/RowIterator.php | 26 ++++++++++++++++-- tests/Spout/Reader/XLSX/ReaderTest.php | 15 ++++++++++ .../sheet_with_missing_cell_reference.xlsx | Bin 0 -> 5015 bytes 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/resources/xlsx/sheet_with_missing_cell_reference.xlsx diff --git a/src/Spout/Reader/XLSX/RowIterator.php b/src/Spout/Reader/XLSX/RowIterator.php index c7491ac..a7c70e6 100644 --- a/src/Spout/Reader/XLSX/RowIterator.php +++ b/src/Spout/Reader/XLSX/RowIterator.php @@ -55,6 +55,9 @@ class RowIterator implements IteratorInterface /** @var int The number of columns the sheet has (0 meaning undefined) */ protected $numColumns = 0; + /** @var int Last column index processed (zero-based) */ + protected $lastColumnIndexProcessed = -1; + /** * @param string $filePath Path of the XLSX file being read * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml @@ -144,6 +147,9 @@ class RowIterator implements IteratorInterface } else if ($this->xmlReader->isPositionedOnStartingNode(self::XML_NODE_ROW)) { // Start of the row description + // Reset index of the last processed column + $this->lastColumnIndexProcessed = -1; + // Read spans info if present $numberOfColumnsForRow = $this->numColumns; $spans = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_SPANS); // returns '1:5' for instance @@ -155,12 +161,13 @@ class RowIterator implements IteratorInterface } else if ($this->xmlReader->isPositionedOnStartingNode(self::XML_NODE_CELL)) { // Start of a cell description - $currentCellIndex = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_CELL_INDEX); - $currentColumnIndex = CellHelper::getColumnIndexFromCellIndex($currentCellIndex); + $currentColumnIndex = $this->getCellIndex($this->xmlReader); $node = $this->xmlReader->expand(); $rowData[$currentColumnIndex] = $this->getCellValue($node); + $this->lastColumnIndexProcessed = $currentColumnIndex; + } else if ($this->xmlReader->isPositionedOnEndingNode(self::XML_NODE_ROW)) { // End of the row description // If needed, we fill the empty cells @@ -182,6 +189,21 @@ class RowIterator implements IteratorInterface $this->rowDataBuffer = $rowData; } + /** + * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" tag + * @return int + * @throws \Box\Spout\Common\Exception\InvalidArgumentException When the given cell index is invalid + */ + protected function getCellIndex($xmlReader) + { + // Get "r" attribute if present (from something like + $currentCellIndex = $xmlReader->getAttribute(self::XML_ATTRIBUTE_CELL_INDEX); + + return ($currentCellIndex !== null) ? + CellHelper::getColumnIndexFromCellIndex($currentCellIndex) : + $this->lastColumnIndexProcessed + 1; + } + /** * Returns the (unescaped) correctly marshalled, cell value associated to the given XML node. * diff --git a/tests/Spout/Reader/XLSX/ReaderTest.php b/tests/Spout/Reader/XLSX/ReaderTest.php index c705f2f..3fa6ec1 100644 --- a/tests/Spout/Reader/XLSX/ReaderTest.php +++ b/tests/Spout/Reader/XLSX/ReaderTest.php @@ -140,6 +140,21 @@ class ReaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expectedRows, $allRows); } + /** + * @return void + */ + public function testReadShouldSupportFilesWithoutCellReference() + { + // file where the cell definition does not have a "r" attribute + // as in ... + $allRows = $this->getAllRowsForFile('sheet_with_missing_cell_reference.xlsx'); + + $expectedRows = [ + ['s1--A1'], + ]; + $this->assertEquals($expectedRows, $allRows); + } + /** * @return void */ diff --git a/tests/resources/xlsx/sheet_with_missing_cell_reference.xlsx b/tests/resources/xlsx/sheet_with_missing_cell_reference.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..4e86cda28225a41372dcc870ba307eafda3857e2 GIT binary patch literal 5015 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 literal 0 HcmV?d00001