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