[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 3 namespace Box\Spout\Reader\XLSX; 4 5 use Box\Spout\Common\Exception\IOException; 6 use Box\Spout\Reader\Exception\XMLProcessingException; 7 use Box\Spout\Reader\IteratorInterface; 8 use Box\Spout\Reader\Wrapper\XMLReader; 9 use Box\Spout\Reader\XLSX\Helper\CellHelper; 10 use Box\Spout\Reader\XLSX\Helper\CellValueFormatter; 11 use Box\Spout\Reader\XLSX\Helper\StyleHelper; 12 13 /** 14 * Class RowIterator 15 * 16 * @package Box\Spout\Reader\XLSX 17 */ 18 class RowIterator implements IteratorInterface 19 { 20 /** Definition of XML nodes names used to parse data */ 21 const XML_NODE_DIMENSION = 'dimension'; 22 const XML_NODE_WORKSHEET = 'worksheet'; 23 const XML_NODE_ROW = 'row'; 24 const XML_NODE_CELL = 'c'; 25 26 /** Definition of XML attributes used to parse data */ 27 const XML_ATTRIBUTE_REF = 'ref'; 28 const XML_ATTRIBUTE_SPANS = 'spans'; 29 const XML_ATTRIBUTE_CELL_INDEX = 'r'; 30 31 /** @var string Path of the XLSX file being read */ 32 protected $filePath; 33 34 /** @var string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml */ 35 protected $sheetDataXMLFilePath; 36 37 /** @var \Box\Spout\Reader\Wrapper\XMLReader The XMLReader object that will help read sheet's XML data */ 38 protected $xmlReader; 39 40 /** @var Helper\CellValueFormatter Helper to format cell values */ 41 protected $cellValueFormatter; 42 43 /** @var Helper\StyleHelper $styleHelper Helper to work with styles */ 44 protected $styleHelper; 45 46 /** @var int Number of read rows */ 47 protected $numReadRows = 0; 48 49 /** @var array|null Buffer used to store the row data, while checking if there are more rows to read */ 50 protected $rowDataBuffer = null; 51 52 /** @var bool Indicates whether all rows have been read */ 53 protected $hasReachedEndOfFile = false; 54 55 /** @var int The number of columns the sheet has (0 meaning undefined) */ 56 protected $numColumns = 0; 57 58 /** 59 * @param string $filePath Path of the XLSX file being read 60 * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml 61 * @param Helper\SharedStringsHelper $sharedStringsHelper Helper to work with shared strings 62 */ 63 public function __construct($filePath, $sheetDataXMLFilePath, $sharedStringsHelper) 64 { 65 $this->filePath = $filePath; 66 $this->sheetDataXMLFilePath = $this->normalizeSheetDataXMLFilePath($sheetDataXMLFilePath); 67 68 $this->xmlReader = new XMLReader(); 69 70 $this->styleHelper = new StyleHelper($filePath); 71 $this->cellValueFormatter = new CellValueFormatter($sharedStringsHelper, $this->styleHelper); 72 } 73 74 /** 75 * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml 76 * @return string Path of the XML file containing the sheet data, 77 * without the leading slash. 78 */ 79 protected function normalizeSheetDataXMLFilePath($sheetDataXMLFilePath) 80 { 81 return ltrim($sheetDataXMLFilePath, '/'); 82 } 83 84 /** 85 * Rewind the Iterator to the first element. 86 * Initializes the XMLReader object that reads the associated sheet data. 87 * The XMLReader is configured to be safe from billion laughs attack. 88 * @link http://php.net/manual/en/iterator.rewind.php 89 * 90 * @return void 91 * @throws \Box\Spout\Common\Exception\IOException If the sheet data XML cannot be read 92 */ 93 public function rewind() 94 { 95 $this->xmlReader->close(); 96 97 $sheetDataFilePath = 'zip://' . $this->filePath . '#' . $this->sheetDataXMLFilePath; 98 if ($this->xmlReader->open($sheetDataFilePath) === false) { 99 throw new IOException("Could not open \"{$this->sheetDataXMLFilePath}\"."); 100 } 101 102 $this->numReadRows = 0; 103 $this->rowDataBuffer = null; 104 $this->hasReachedEndOfFile = false; 105 $this->numColumns = 0; 106 107 $this->next(); 108 } 109 110 /** 111 * Checks if current position is valid 112 * @link http://php.net/manual/en/iterator.valid.php 113 * 114 * @return boolean 115 */ 116 public function valid() 117 { 118 return (!$this->hasReachedEndOfFile); 119 } 120 121 /** 122 * Move forward to next element. Empty rows will be skipped. 123 * @link http://php.net/manual/en/iterator.next.php 124 * 125 * @return void 126 * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If a shared string was not found 127 * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML 128 */ 129 public function next() 130 { 131 $rowData = []; 132 133 try { 134 while ($this->xmlReader->read()) { 135 if ($this->xmlReader->isPositionedOnStartingNode(self::XML_NODE_DIMENSION)) { 136 // Read dimensions of the sheet 137 $dimensionRef = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_REF); // returns 'A1:M13' for instance (or 'A1' for empty sheet) 138 if (preg_match('/[A-Z\d]+:([A-Z\d]+)/', $dimensionRef, $matches)) { 139 $lastCellIndex = $matches[1]; 140 $this->numColumns = CellHelper::getColumnIndexFromCellIndex($lastCellIndex) + 1; 141 } 142 143 } else if ($this->xmlReader->isPositionedOnStartingNode(self::XML_NODE_ROW)) { 144 // Start of the row description 145 146 // Read spans info if present 147 $numberOfColumnsForRow = $this->numColumns; 148 $spans = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_SPANS); // returns '1:5' for instance 149 if ($spans) { 150 list(, $numberOfColumnsForRow) = explode(':', $spans); 151 $numberOfColumnsForRow = intval($numberOfColumnsForRow); 152 } 153 $rowData = ($numberOfColumnsForRow !== 0) ? array_fill(0, $numberOfColumnsForRow, '') : []; 154 155 } else if ($this->xmlReader->isPositionedOnStartingNode(self::XML_NODE_CELL)) { 156 // Start of a cell description 157 $currentCellIndex = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_CELL_INDEX); 158 $currentColumnIndex = CellHelper::getColumnIndexFromCellIndex($currentCellIndex); 159 160 $node = $this->xmlReader->expand(); 161 $rowData[$currentColumnIndex] = $this->getCellValue($node); 162 163 } else if ($this->xmlReader->isPositionedOnEndingNode(self::XML_NODE_ROW)) { 164 // End of the row description 165 // If needed, we fill the empty cells 166 $rowData = ($this->numColumns !== 0) ? $rowData : CellHelper::fillMissingArrayIndexes($rowData); 167 $this->numReadRows++; 168 break; 169 170 } else if ($this->xmlReader->isPositionedOnEndingNode(self::XML_NODE_WORKSHEET)) { 171 // The closing "</worksheet>" marks the end of the file 172 $this->hasReachedEndOfFile = true; 173 break; 174 } 175 } 176 177 } catch (XMLProcessingException $exception) { 178 throw new IOException("The {$this->sheetDataXMLFilePath} file cannot be read. [{$exception->getMessage()}]"); 179 } 180 181 $this->rowDataBuffer = $rowData; 182 } 183 184 /** 185 * Returns the (unescaped) correctly marshalled, cell value associated to the given XML node. 186 * 187 * @param \DOMNode $node 188 * @return string|int|float|bool|\DateTime|null The value associated with the cell (null when the cell has an error) 189 */ 190 protected function getCellValue($node) 191 { 192 return $this->cellValueFormatter->extractAndFormatNodeValue($node); 193 } 194 195 /** 196 * Return the current element, from the buffer. 197 * @link http://php.net/manual/en/iterator.current.php 198 * 199 * @return array|null 200 */ 201 public function current() 202 { 203 return $this->rowDataBuffer; 204 } 205 206 /** 207 * Return the key of the current element 208 * @link http://php.net/manual/en/iterator.key.php 209 * 210 * @return int 211 */ 212 public function key() 213 { 214 return $this->numReadRows; 215 } 216 217 218 /** 219 * Cleans up what was created to iterate over the object. 220 * 221 * @return void 222 */ 223 public function end() 224 { 225 $this->xmlReader->close(); 226 } 227 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |