1 <?php 2 3 declare(strict_types=1); 4 5 namespace OpenSpout\Writer\Common\Manager; 6 7 use OpenSpout\Common\Entity\Row; 8 use OpenSpout\Common\Exception\IOException; 9 use OpenSpout\Common\Helper\StringHelper; 10 use OpenSpout\Writer\Common\AbstractOptions; 11 use OpenSpout\Writer\Common\Entity\Sheet; 12 use OpenSpout\Writer\Common\Entity\Workbook; 13 use OpenSpout\Writer\Common\Entity\Worksheet; 14 use OpenSpout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface; 15 use OpenSpout\Writer\Common\Manager\Style\StyleManagerInterface; 16 use OpenSpout\Writer\Common\Manager\Style\StyleMerger; 17 use OpenSpout\Writer\Exception\SheetNotFoundException; 18 19 /** 20 * @internal 21 */ 22 abstract class AbstractWorkbookManager implements WorkbookManagerInterface 23 { 24 protected WorksheetManagerInterface $worksheetManager; 25 26 /** @var StyleManagerInterface Manages styles */ 27 protected StyleManagerInterface $styleManager; 28 29 /** @var FileSystemWithRootFolderHelperInterface Helper to perform file system operations */ 30 protected FileSystemWithRootFolderHelperInterface $fileSystemHelper; 31 32 protected AbstractOptions $options; 33 34 /** @var Workbook The workbook to manage */ 35 private Workbook $workbook; 36 37 /** @var StyleMerger Helper to merge styles */ 38 private StyleMerger $styleMerger; 39 40 /** @var Worksheet The worksheet where data will be written to */ 41 private Worksheet $currentWorksheet; 42 43 public function __construct( 44 Workbook $workbook, 45 AbstractOptions $options, 46 WorksheetManagerInterface $worksheetManager, 47 StyleManagerInterface $styleManager, 48 StyleMerger $styleMerger, 49 FileSystemWithRootFolderHelperInterface $fileSystemHelper 50 ) { 51 $this->workbook = $workbook; 52 $this->options = $options; 53 $this->worksheetManager = $worksheetManager; 54 $this->styleManager = $styleManager; 55 $this->styleMerger = $styleMerger; 56 $this->fileSystemHelper = $fileSystemHelper; 57 } 58 59 /** 60 * Creates a new sheet in the workbook and make it the current sheet. 61 * The writing will resume where it stopped (i.e. data won't be truncated). 62 * 63 * @return Worksheet The created sheet 64 */ 65 final public function addNewSheetAndMakeItCurrent(): Worksheet 66 { 67 $worksheet = $this->addNewSheet(); 68 $this->setCurrentWorksheet($worksheet); 69 70 return $worksheet; 71 } 72 73 /** 74 * @return Worksheet[] All the workbook's sheets 75 */ 76 final public function getWorksheets(): array 77 { 78 return $this->workbook->getWorksheets(); 79 } 80 81 /** 82 * Returns the current sheet. 83 * 84 * @return Worksheet The current sheet 85 */ 86 final public function getCurrentWorksheet(): Worksheet 87 { 88 return $this->currentWorksheet; 89 } 90 91 /** 92 * Sets the given sheet as the current one. New data will be written to this sheet. 93 * The writing will resume where it stopped (i.e. data won't be truncated). 94 * 95 * @param Sheet $sheet The "external" sheet to set as current 96 * 97 * @throws SheetNotFoundException If the given sheet does not exist in the workbook 98 */ 99 final public function setCurrentSheet(Sheet $sheet): void 100 { 101 $worksheet = $this->getWorksheetFromExternalSheet($sheet); 102 if (null !== $worksheet) { 103 $this->currentWorksheet = $worksheet; 104 } else { 105 throw new SheetNotFoundException('The given sheet does not exist in the workbook.'); 106 } 107 } 108 109 /** 110 * Adds a row to the current sheet. 111 * If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination 112 * with the creation of new worksheets if one worksheet has reached its maximum capicity. 113 * 114 * @param Row $row The row to be added 115 * 116 * @throws IOException If trying to create a new sheet and unable to open the sheet for writing 117 * @throws \OpenSpout\Common\Exception\InvalidArgumentException 118 */ 119 final public function addRowToCurrentWorksheet(Row $row): void 120 { 121 $currentWorksheet = $this->getCurrentWorksheet(); 122 if ($this->hasCurrentWorksheetReachedMaxRows()) { 123 if (!$this->options->SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY) { 124 return; 125 } 126 127 $currentWorksheet = $this->addNewSheetAndMakeItCurrent(); 128 } 129 130 $this->addRowToWorksheet($currentWorksheet, $row); 131 $currentWorksheet->getExternalSheet()->incrementWrittenRowCount(); 132 } 133 134 /** 135 * Closes the workbook and all its associated sheets. 136 * All the necessary files are written to disk and zipped together to create the final file. 137 * All the temporary files are then deleted. 138 * 139 * @param resource $finalFilePointer Pointer to the spreadsheet that will be created 140 */ 141 final public function close($finalFilePointer): void 142 { 143 $this->closeAllWorksheets(); 144 $this->closeRemainingObjects(); 145 $this->writeAllFilesToDiskAndZipThem($finalFilePointer); 146 $this->cleanupTempFolder(); 147 } 148 149 /** 150 * @return int Maximum number of rows/columns a sheet can contain 151 */ 152 abstract protected function getMaxRowsPerWorksheet(): int; 153 154 /** 155 * Closes custom objects that are still opened. 156 */ 157 protected function closeRemainingObjects(): void 158 { 159 // do nothing by default 160 } 161 162 /** 163 * Writes all the necessary files to disk and zip them together to create the final file. 164 * 165 * @param resource $finalFilePointer Pointer to the spreadsheet that will be created 166 */ 167 abstract protected function writeAllFilesToDiskAndZipThem($finalFilePointer): void; 168 169 /** 170 * @return string The file path where the data for the given sheet will be stored 171 */ 172 private function getWorksheetFilePath(Sheet $sheet): string 173 { 174 $sheetsContentTempFolder = $this->fileSystemHelper->getSheetsContentTempFolder(); 175 176 return $sheetsContentTempFolder.\DIRECTORY_SEPARATOR.'sheet'.(1 + $sheet->getIndex()).'.xml'; 177 } 178 179 /** 180 * Deletes the root folder created in the temp folder and all its contents. 181 */ 182 private function cleanupTempFolder(): void 183 { 184 $rootFolder = $this->fileSystemHelper->getRootFolder(); 185 $this->fileSystemHelper->deleteFolderRecursively($rootFolder); 186 } 187 188 /** 189 * Creates a new sheet in the workbook. The current sheet remains unchanged. 190 * 191 * @return Worksheet The created sheet 192 * 193 * @throws \OpenSpout\Common\Exception\IOException If unable to open the sheet for writing 194 */ 195 private function addNewSheet(): Worksheet 196 { 197 $worksheets = $this->getWorksheets(); 198 199 $newSheetIndex = \count($worksheets); 200 $sheetManager = new SheetManager(StringHelper::factory()); 201 $sheet = new Sheet($newSheetIndex, $this->workbook->getInternalId(), $sheetManager); 202 203 $worksheetFilePath = $this->getWorksheetFilePath($sheet); 204 $worksheet = new Worksheet($worksheetFilePath, $sheet); 205 206 $this->worksheetManager->startSheet($worksheet); 207 208 $worksheets[] = $worksheet; 209 $this->workbook->setWorksheets($worksheets); 210 211 return $worksheet; 212 } 213 214 private function setCurrentWorksheet(Worksheet $worksheet): void 215 { 216 $this->currentWorksheet = $worksheet; 217 } 218 219 /** 220 * Returns the worksheet associated to the given external sheet. 221 * 222 * @return null|Worksheet the worksheet associated to the given external sheet or null if not found 223 */ 224 private function getWorksheetFromExternalSheet(Sheet $sheet): ?Worksheet 225 { 226 $worksheetFound = null; 227 228 foreach ($this->getWorksheets() as $worksheet) { 229 if ($worksheet->getExternalSheet() === $sheet) { 230 $worksheetFound = $worksheet; 231 232 break; 233 } 234 } 235 236 return $worksheetFound; 237 } 238 239 /** 240 * @return bool whether the current worksheet has reached the maximum number of rows per sheet 241 */ 242 private function hasCurrentWorksheetReachedMaxRows(): bool 243 { 244 $currentWorksheet = $this->getCurrentWorksheet(); 245 246 return $currentWorksheet->getLastWrittenRowIndex() >= $this->getMaxRowsPerWorksheet(); 247 } 248 249 /** 250 * Adds a row to the given sheet. 251 * 252 * @param Worksheet $worksheet Worksheet to write the row to 253 * @param Row $row The row to be added 254 * 255 * @throws IOException 256 * @throws \OpenSpout\Common\Exception\InvalidArgumentException 257 */ 258 private function addRowToWorksheet(Worksheet $worksheet, Row $row): void 259 { 260 $this->applyDefaultRowStyle($row); 261 $this->worksheetManager->addRow($worksheet, $row); 262 263 // update max num columns for the worksheet 264 $currentMaxNumColumns = $worksheet->getMaxNumColumns(); 265 $cellsCount = $row->getNumCells(); 266 $worksheet->setMaxNumColumns(max($currentMaxNumColumns, $cellsCount)); 267 } 268 269 private function applyDefaultRowStyle(Row $row): void 270 { 271 $mergedStyle = $this->styleMerger->merge( 272 $row->getStyle(), 273 $this->options->DEFAULT_ROW_STYLE 274 ); 275 $row->setStyle($mergedStyle); 276 } 277 278 /** 279 * Closes all workbook's associated sheets. 280 */ 281 private function closeAllWorksheets(): void 282 { 283 $worksheets = $this->getWorksheets(); 284 285 foreach ($worksheets as $worksheet) { 286 $this->worksheetManager->close($worksheet); 287 } 288 } 289 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body