[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/spout/src/Spout/Reader/XLSX/ -> RowIterator.php (source)

   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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1