Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401]
1 <?php 2 3 namespace Box\Spout\Reader\ODS; 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\ODS\Creator\InternalEntityFactory; 9 use Box\Spout\Reader\ODS\Helper\SettingsHelper; 10 use Box\Spout\Reader\Wrapper\XMLReader; 11 12 /** 13 * Class SheetIterator 14 * Iterate over ODS sheet. 15 */ 16 class SheetIterator implements IteratorInterface 17 { 18 const CONTENT_XML_FILE_PATH = 'content.xml'; 19 20 const XML_STYLE_NAMESPACE = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'; 21 22 /** Definition of XML nodes name and attribute used to parse sheet data */ 23 const XML_NODE_AUTOMATIC_STYLES = 'office:automatic-styles'; 24 const XML_NODE_STYLE_TABLE_PROPERTIES = 'table-properties'; 25 const XML_NODE_TABLE = 'table:table'; 26 const XML_ATTRIBUTE_STYLE_NAME = 'style:name'; 27 const XML_ATTRIBUTE_TABLE_NAME = 'table:name'; 28 const XML_ATTRIBUTE_TABLE_STYLE_NAME = 'table:style-name'; 29 const XML_ATTRIBUTE_TABLE_DISPLAY = 'table:display'; 30 31 /** @var string $filePath Path of the file to be read */ 32 protected $filePath; 33 34 /** @var \Box\Spout\Common\Manager\OptionsManagerInterface Reader's options manager */ 35 protected $optionsManager; 36 37 /** @var InternalEntityFactory $entityFactory Factory to create entities */ 38 protected $entityFactory; 39 40 /** @var XMLReader The XMLReader object that will help read sheet's XML data */ 41 protected $xmlReader; 42 43 /** @var \Box\Spout\Common\Helper\Escaper\ODS Used to unescape XML data */ 44 protected $escaper; 45 46 /** @var bool Whether there are still at least a sheet to be read */ 47 protected $hasFoundSheet; 48 49 /** @var int The index of the sheet being read (zero-based) */ 50 protected $currentSheetIndex; 51 52 /** @var string The name of the sheet that was defined as active */ 53 protected $activeSheetName; 54 55 /** @var array Associative array [STYLE_NAME] => [IS_SHEET_VISIBLE] */ 56 protected $sheetsVisibility; 57 58 /** 59 * @param string $filePath Path of the file to be read 60 * @param \Box\Spout\Common\Manager\OptionsManagerInterface $optionsManager 61 * @param \Box\Spout\Common\Helper\Escaper\ODS $escaper Used to unescape XML data 62 * @param SettingsHelper $settingsHelper Helper to get data from "settings.xml" 63 * @param InternalEntityFactory $entityFactory Factory to create entities 64 */ 65 public function __construct($filePath, $optionsManager, $escaper, $settingsHelper, $entityFactory) 66 { 67 $this->filePath = $filePath; 68 $this->optionsManager = $optionsManager; 69 $this->entityFactory = $entityFactory; 70 $this->xmlReader = $entityFactory->createXMLReader(); 71 $this->escaper = $escaper; 72 $this->activeSheetName = $settingsHelper->getActiveSheetName($filePath); 73 } 74 75 /** 76 * Rewind the Iterator to the first element 77 * @see http://php.net/manual/en/iterator.rewind.php 78 * 79 * @throws \Box\Spout\Common\Exception\IOException If unable to open the XML file containing sheets' data 80 * @return void 81 */ 82 public function rewind() 83 { 84 $this->xmlReader->close(); 85 86 if ($this->xmlReader->openFileInZip($this->filePath, self::CONTENT_XML_FILE_PATH) === false) { 87 $contentXmlFilePath = $this->filePath . '#' . self::CONTENT_XML_FILE_PATH; 88 throw new IOException("Could not open \"{$contentXmlFilePath}\"."); 89 } 90 91 try { 92 $this->sheetsVisibility = $this->readSheetsVisibility(); 93 $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); 94 } catch (XMLProcessingException $exception) { 95 throw new IOException("The content.xml file is invalid and cannot be read. [{$exception->getMessage()}]"); 96 } 97 98 $this->currentSheetIndex = 0; 99 } 100 101 /** 102 * Extracts the visibility of the sheets 103 * 104 * @return array Associative array [STYLE_NAME] => [IS_SHEET_VISIBLE] 105 */ 106 private function readSheetsVisibility() 107 { 108 $sheetsVisibility = []; 109 110 $this->xmlReader->readUntilNodeFound(self::XML_NODE_AUTOMATIC_STYLES); 111 $automaticStylesNode = $this->xmlReader->expand(); 112 113 $tableStyleNodes = $automaticStylesNode->getElementsByTagNameNS(self::XML_STYLE_NAMESPACE, self::XML_NODE_STYLE_TABLE_PROPERTIES); 114 115 /** @var \DOMElement $tableStyleNode */ 116 foreach ($tableStyleNodes as $tableStyleNode) { 117 $isSheetVisible = ($tableStyleNode->getAttribute(self::XML_ATTRIBUTE_TABLE_DISPLAY) !== 'false'); 118 119 $parentStyleNode = $tableStyleNode->parentNode; 120 $styleName = $parentStyleNode->getAttribute(self::XML_ATTRIBUTE_STYLE_NAME); 121 122 $sheetsVisibility[$styleName] = $isSheetVisible; 123 } 124 125 return $sheetsVisibility; 126 } 127 128 /** 129 * Checks if current position is valid 130 * @see http://php.net/manual/en/iterator.valid.php 131 * 132 * @return bool 133 */ 134 public function valid() 135 { 136 return $this->hasFoundSheet; 137 } 138 139 /** 140 * Move forward to next element 141 * @see http://php.net/manual/en/iterator.next.php 142 * 143 * @return void 144 */ 145 public function next() 146 { 147 $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); 148 149 if ($this->hasFoundSheet) { 150 $this->currentSheetIndex++; 151 } 152 } 153 154 /** 155 * Return the current element 156 * @see http://php.net/manual/en/iterator.current.php 157 * 158 * @return \Box\Spout\Reader\ODS\Sheet 159 */ 160 public function current() 161 { 162 $escapedSheetName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_NAME); 163 $sheetName = $this->escaper->unescape($escapedSheetName); 164 165 $isSheetActive = $this->isSheetActive($sheetName, $this->currentSheetIndex, $this->activeSheetName); 166 167 $sheetStyleName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_STYLE_NAME); 168 $isSheetVisible = $this->isSheetVisible($sheetStyleName); 169 170 return $this->entityFactory->createSheet( 171 $this->xmlReader, 172 $this->currentSheetIndex, 173 $sheetName, 174 $isSheetActive, 175 $isSheetVisible, 176 $this->optionsManager 177 ); 178 } 179 180 /** 181 * Returns whether the current sheet was defined as the active one 182 * 183 * @param string $sheetName Name of the current sheet 184 * @param int $sheetIndex Index of the current sheet 185 * @param string|null $activeSheetName Name of the sheet that was defined as active or NULL if none defined 186 * @return bool Whether the current sheet was defined as the active one 187 */ 188 private function isSheetActive($sheetName, $sheetIndex, $activeSheetName) 189 { 190 // The given sheet is active if its name matches the defined active sheet's name 191 // or if no information about the active sheet was found, it defaults to the first sheet. 192 return ( 193 ($activeSheetName === null && $sheetIndex === 0) || 194 ($activeSheetName === $sheetName) 195 ); 196 } 197 198 /** 199 * Returns whether the current sheet is visible 200 * 201 * @param string $sheetStyleName Name of the sheet style 202 * @return bool Whether the current sheet is visible 203 */ 204 private function isSheetVisible($sheetStyleName) 205 { 206 return isset($this->sheetsVisibility[$sheetStyleName]) ? 207 $this->sheetsVisibility[$sheetStyleName] : 208 true; 209 } 210 211 /** 212 * Return the key of the current element 213 * @see http://php.net/manual/en/iterator.key.php 214 * 215 * @return int 216 */ 217 public function key() 218 { 219 return $this->currentSheetIndex + 1; 220 } 221 222 /** 223 * Cleans up what was created to iterate over the object. 224 * 225 * @return void 226 */ 227 public function end() 228 { 229 $this->xmlReader->close(); 230 } 231 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body