Differences Between: [Versions 310 and 311] [Versions 39 and 311]
1 <?php 2 3 namespace Box\Spout\Writer; 4 5 use Box\Spout\Common\Creator\HelperFactory; 6 use Box\Spout\Common\Entity\Row; 7 use Box\Spout\Common\Entity\Style\Style; 8 use Box\Spout\Common\Exception\InvalidArgumentException; 9 use Box\Spout\Common\Exception\IOException; 10 use Box\Spout\Common\Exception\SpoutException; 11 use Box\Spout\Common\Helper\GlobalFunctionsHelper; 12 use Box\Spout\Common\Manager\OptionsManagerInterface; 13 use Box\Spout\Writer\Common\Entity\Options; 14 use Box\Spout\Writer\Exception\WriterAlreadyOpenedException; 15 use Box\Spout\Writer\Exception\WriterNotOpenedException; 16 17 /** 18 * Class WriterAbstract 19 * 20 * @abstract 21 */ 22 abstract class WriterAbstract implements WriterInterface 23 { 24 /** @var string Path to the output file */ 25 protected $outputFilePath; 26 27 /** @var resource Pointer to the file/stream we will write to */ 28 protected $filePointer; 29 30 /** @var bool Indicates whether the writer has been opened or not */ 31 protected $isWriterOpened = false; 32 33 /** @var GlobalFunctionsHelper Helper to work with global functions */ 34 protected $globalFunctionsHelper; 35 36 /** @var HelperFactory */ 37 protected $helperFactory; 38 39 /** @var OptionsManagerInterface Writer options manager */ 40 protected $optionsManager; 41 42 /** @var string Content-Type value for the header - to be defined by child class */ 43 protected static $headerContentType; 44 45 /** 46 * @param OptionsManagerInterface $optionsManager 47 * @param GlobalFunctionsHelper $globalFunctionsHelper 48 * @param HelperFactory $helperFactory 49 */ 50 public function __construct( 51 OptionsManagerInterface $optionsManager, 52 GlobalFunctionsHelper $globalFunctionsHelper, 53 HelperFactory $helperFactory 54 ) { 55 $this->optionsManager = $optionsManager; 56 $this->globalFunctionsHelper = $globalFunctionsHelper; 57 $this->helperFactory = $helperFactory; 58 } 59 60 /** 61 * Opens the streamer and makes it ready to accept data. 62 * 63 * @throws IOException If the writer cannot be opened 64 * @return void 65 */ 66 abstract protected function openWriter(); 67 68 /** 69 * Adds a row to the currently opened writer. 70 * 71 * @param Row $row The row containing cells and styles 72 * @throws WriterNotOpenedException If the workbook is not created yet 73 * @throws IOException If unable to write data 74 * @return void 75 */ 76 abstract protected function addRowToWriter(Row $row); 77 78 /** 79 * Closes the streamer, preventing any additional writing. 80 * 81 * @return void 82 */ 83 abstract protected function closeWriter(); 84 85 /** 86 * {@inheritdoc} 87 */ 88 public function setDefaultRowStyle(Style $defaultStyle) 89 { 90 $this->optionsManager->setOption(Options::DEFAULT_ROW_STYLE, $defaultStyle); 91 92 return $this; 93 } 94 95 /** 96 * {@inheritdoc} 97 */ 98 public function openToFile($outputFilePath) 99 { 100 $this->outputFilePath = $outputFilePath; 101 102 $this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'wb+'); 103 $this->throwIfFilePointerIsNotAvailable(); 104 105 $this->openWriter(); 106 $this->isWriterOpened = true; 107 108 return $this; 109 } 110 111 /** 112 * @codeCoverageIgnore 113 * {@inheritdoc} 114 */ 115 public function openToBrowser($outputFileName) 116 { 117 $this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName); 118 119 $this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w'); 120 $this->throwIfFilePointerIsNotAvailable(); 121 122 // Clear any previous output (otherwise the generated file will be corrupted) 123 // @see https://github.com/box/spout/issues/241 124 $this->globalFunctionsHelper->ob_end_clean(); 125 126 /* 127 * Set headers 128 * 129 * For newer browsers such as Firefox, Chrome, Opera, Safari, etc., they all support and use `filename*` 130 * specified by the new standard, even if they do not automatically decode filename; it does not matter; 131 * and for older versions of Internet Explorer, they are not recognized `filename*`, will automatically 132 * ignore it and use the old `filename` (the only minor flaw is that there must be an English suffix name). 133 * In this way, the multi-browser multi-language compatibility problem is perfectly solved, which does not 134 * require UA judgment and is more in line with the standard. 135 * 136 * @see https://github.com/box/spout/issues/745 137 * @see https://tools.ietf.org/html/rfc6266 138 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition 139 */ 140 $this->globalFunctionsHelper->header('Content-Type: ' . static::$headerContentType); 141 $this->globalFunctionsHelper->header( 142 'Content-Disposition: attachment; ' . 143 'filename="' . rawurlencode($this->outputFilePath) . '"; ' . 144 'filename*=UTF-8\'\'' . rawurlencode($this->outputFilePath) 145 ); 146 147 /* 148 * When forcing the download of a file over SSL,IE8 and lower browsers fail 149 * if the Cache-Control and Pragma headers are not set. 150 * 151 * @see http://support.microsoft.com/KB/323308 152 * @see https://github.com/liuggio/ExcelBundle/issues/45 153 */ 154 $this->globalFunctionsHelper->header('Cache-Control: max-age=0'); 155 $this->globalFunctionsHelper->header('Pragma: public'); 156 157 $this->openWriter(); 158 $this->isWriterOpened = true; 159 160 return $this; 161 } 162 163 /** 164 * Checks if the pointer to the file/stream to write to is available. 165 * Will throw an exception if not available. 166 * 167 * @throws IOException If the pointer is not available 168 * @return void 169 */ 170 protected function throwIfFilePointerIsNotAvailable() 171 { 172 if (!$this->filePointer) { 173 throw new IOException('File pointer has not be opened'); 174 } 175 } 176 177 /** 178 * Checks if the writer has already been opened, since some actions must be done before it gets opened. 179 * Throws an exception if already opened. 180 * 181 * @param string $message Error message 182 * @throws WriterAlreadyOpenedException If the writer was already opened and must not be. 183 * @return void 184 */ 185 protected function throwIfWriterAlreadyOpened($message) 186 { 187 if ($this->isWriterOpened) { 188 throw new WriterAlreadyOpenedException($message); 189 } 190 } 191 192 /** 193 * {@inheritdoc} 194 */ 195 public function addRow(Row $row) 196 { 197 if ($this->isWriterOpened) { 198 try { 199 $this->addRowToWriter($row); 200 } catch (SpoutException $e) { 201 // if an exception occurs while writing data, 202 // close the writer and remove all files created so far. 203 $this->closeAndAttemptToCleanupAllFiles(); 204 205 // re-throw the exception to alert developers of the error 206 throw $e; 207 } 208 } else { 209 throw new WriterNotOpenedException('The writer needs to be opened before adding row.'); 210 } 211 212 return $this; 213 } 214 215 /** 216 * {@inheritdoc} 217 */ 218 public function addRows(array $rows) 219 { 220 foreach ($rows as $row) { 221 if (!$row instanceof Row) { 222 $this->closeAndAttemptToCleanupAllFiles(); 223 throw new InvalidArgumentException('The input should be an array of Row'); 224 } 225 226 $this->addRow($row); 227 } 228 229 return $this; 230 } 231 232 /** 233 * {@inheritdoc} 234 */ 235 public function close() 236 { 237 if (!$this->isWriterOpened) { 238 return; 239 } 240 241 $this->closeWriter(); 242 243 if (\is_resource($this->filePointer)) { 244 $this->globalFunctionsHelper->fclose($this->filePointer); 245 } 246 247 $this->isWriterOpened = false; 248 } 249 250 /** 251 * Closes the writer and attempts to cleanup all files that were 252 * created during the writing process (temp files & final file). 253 * 254 * @return void 255 */ 256 private function closeAndAttemptToCleanupAllFiles() 257 { 258 // close the writer, which should remove all temp files 259 $this->close(); 260 261 // remove output file if it was created 262 if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) { 263 $outputFolderPath = \dirname($this->outputFilePath); 264 $fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath); 265 $fileSystemHelper->deleteFile($this->outputFilePath); 266 } 267 } 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body