See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 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 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 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 #[\ReturnTypeWillChange] 83 public function rewind() 84 { 85 $this->xmlReader->close(); 86 87 if ($this->xmlReader->openFileInZip($this->filePath, self::CONTENT_XML_FILE_PATH) === false) { 88 $contentXmlFilePath = $this->filePath . '#' . self::CONTENT_XML_FILE_PATH; 89 throw new IOException("Could not open \"{$contentXmlFilePath}\"."); 90 } 91 92 try { 93 $this->sheetsVisibility = $this->readSheetsVisibility(); 94 $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); 95 } catch (XMLProcessingException $exception) { 96 throw new IOException("The content.xml file is invalid and cannot be read. [{$exception->getMessage()}]"); 97 } 98 99 $this->currentSheetIndex = 0; 100 } 101 102 /** 103 * Extracts the visibility of the sheets 104 * 105 * @return array Associative array [STYLE_NAME] => [IS_SHEET_VISIBLE] 106 */ 107 private function readSheetsVisibility() 108 { 109 $sheetsVisibility = []; 110 111 $this->xmlReader->readUntilNodeFound(self::XML_NODE_AUTOMATIC_STYLES); 112 $automaticStylesNode = $this->xmlReader->expand(); 113 114 $tableStyleNodes = $automaticStylesNode->getElementsByTagNameNS(self::XML_STYLE_NAMESPACE, self::XML_NODE_STYLE_TABLE_PROPERTIES); 115 116 /** @var \DOMElement $tableStyleNode */ 117 foreach ($tableStyleNodes as $tableStyleNode) { 118 $isSheetVisible = ($tableStyleNode->getAttribute(self::XML_ATTRIBUTE_TABLE_DISPLAY) !== 'false'); 119 120 $parentStyleNode = $tableStyleNode->parentNode; 121 $styleName = $parentStyleNode->getAttribute(self::XML_ATTRIBUTE_STYLE_NAME); 122 123 $sheetsVisibility[$styleName] = $isSheetVisible; 124 } 125 126 return $sheetsVisibility; 127 } 128 129 /** 130 * Checks if current position is valid 131 * @see http://php.net/manual/en/iterator.valid.php 132 * 133 * @return bool 134 */ 135 #[\ReturnTypeWillChange] 136 public function valid() 137 { 138 return $this->hasFoundSheet; 139 } 140 141 /** 142 * Move forward to next element 143 * @see http://php.net/manual/en/iterator.next.php 144 * 145 * @return void 146 */ 147 #[\ReturnTypeWillChange] 148 public function next() 149 { 150 $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); 151 152 if ($this->hasFoundSheet) { 153 $this->currentSheetIndex++; 154 } 155 } 156 157 /** 158 * Return the current element 159 * @see http://php.net/manual/en/iterator.current.php 160 * 161 * @return \Box\Spout\Reader\ODS\Sheet 162 */ 163 #[\ReturnTypeWillChange] 164 public function current() 165 { 166 $escapedSheetName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_NAME); 167 $sheetName = $this->escaper->unescape($escapedSheetName); 168 169 $isSheetActive = $this->isSheetActive($sheetName, $this->currentSheetIndex, $this->activeSheetName); 170 171 $sheetStyleName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_STYLE_NAME); 172 $isSheetVisible = $this->isSheetVisible($sheetStyleName); 173 174 return $this->entityFactory->createSheet( 175 $this->xmlReader, 176 $this->currentSheetIndex, 177 $sheetName, 178 $isSheetActive, 179 $isSheetVisible, 180 $this->optionsManager 181 ); 182 } 183 184 /** 185 * Returns whether the current sheet was defined as the active one 186 * 187 * @param string $sheetName Name of the current sheet 188 * @param int $sheetIndex Index of the current sheet 189 * @param string|null $activeSheetName Name of the sheet that was defined as active or NULL if none defined 190 * @return bool Whether the current sheet was defined as the active one 191 */ 192 private function isSheetActive($sheetName, $sheetIndex, $activeSheetName) 193 { 194 // The given sheet is active if its name matches the defined active sheet's name 195 // or if no information about the active sheet was found, it defaults to the first sheet. 196 return ( 197 ($activeSheetName === null && $sheetIndex === 0) || 198 ($activeSheetName === $sheetName) 199 ); 200 } 201 202 /** 203 * Returns whether the current sheet is visible 204 * 205 * @param string $sheetStyleName Name of the sheet style 206 * @return bool Whether the current sheet is visible 207 */ 208 private function isSheetVisible($sheetStyleName) 209 { 210 return isset($this->sheetsVisibility[$sheetStyleName]) ? 211 $this->sheetsVisibility[$sheetStyleName] : 212 true; 213 } 214 215 /** 216 * Return the key of the current element 217 * @see http://php.net/manual/en/iterator.key.php 218 * 219 * @return int 220 */ 221 #[\ReturnTypeWillChange] 222 public function key() 223 { 224 return $this->currentSheetIndex + 1; 225 } 226 227 /** 228 * Cleans up what was created to iterate over the object. 229 * 230 * @return void 231 */ 232 #[\ReturnTypeWillChange] 233 public function end() 234 { 235 $this->xmlReader->close(); 236 } 237 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body