|
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 $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
|
105 |
|
public function __construct( |
|
51
|
|
|
OptionsManagerInterface $optionsManager, |
|
52
|
|
|
GlobalFunctionsHelper $globalFunctionsHelper, |
|
53
|
|
|
HelperFactory $helperFactory |
|
54
|
|
|
) { |
|
55
|
105 |
|
$this->optionsManager = $optionsManager; |
|
56
|
105 |
|
$this->globalFunctionsHelper = $globalFunctionsHelper; |
|
57
|
105 |
|
$this->helperFactory = $helperFactory; |
|
58
|
105 |
|
} |
|
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
|
2 |
|
public function setDefaultRowStyle(Style $defaultStyle) |
|
89
|
|
|
{ |
|
90
|
2 |
|
$this->optionsManager->setOption(Options::DEFAULT_ROW_STYLE, $defaultStyle); |
|
91
|
|
|
|
|
92
|
2 |
|
return $this; |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
/** |
|
96
|
|
|
* {@inheritdoc} |
|
97
|
|
|
*/ |
|
98
|
90 |
|
public function openToFile($outputFilePath) |
|
99
|
|
|
{ |
|
100
|
90 |
|
$this->outputFilePath = $outputFilePath; |
|
101
|
|
|
|
|
102
|
90 |
|
$this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'wb+'); |
|
103
|
90 |
|
$this->throwIfFilePointerIsNotAvailable(); |
|
104
|
|
|
|
|
105
|
87 |
|
$this->openWriter(); |
|
106
|
87 |
|
$this->isWriterOpened = true; |
|
107
|
|
|
|
|
108
|
87 |
|
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
|
|
|
// Set headers |
|
127
|
|
|
$this->globalFunctionsHelper->header('Content-Type: ' . static::$headerContentType); |
|
128
|
|
|
$this->globalFunctionsHelper->header('Content-Disposition: attachment; filename="' . $this->outputFilePath . '"'); |
|
129
|
|
|
|
|
130
|
|
|
/* |
|
131
|
|
|
* When forcing the download of a file over SSL,IE8 and lower browsers fail |
|
132
|
|
|
* if the Cache-Control and Pragma headers are not set. |
|
133
|
|
|
* |
|
134
|
|
|
* @see http://support.microsoft.com/KB/323308 |
|
135
|
|
|
* @see https://github.com/liuggio/ExcelBundle/issues/45 |
|
136
|
|
|
*/ |
|
137
|
|
|
$this->globalFunctionsHelper->header('Cache-Control: max-age=0'); |
|
138
|
|
|
$this->globalFunctionsHelper->header('Pragma: public'); |
|
139
|
|
|
|
|
140
|
|
|
$this->openWriter(); |
|
141
|
|
|
$this->isWriterOpened = true; |
|
142
|
|
|
|
|
143
|
|
|
return $this; |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
public function openToMemory() |
|
147
|
|
|
{ |
|
148
|
|
|
$this->filePointer = $this->globalFunctionsHelper->fopen('php://temp', 'r+'); |
|
|
|
|
|
|
149
|
|
|
$this->throwIfFilePointerIsNotAvailable(); |
|
150
|
|
|
|
|
151
|
|
|
$this->openWriter(); |
|
152
|
|
|
$this->isWriterOpened = true; |
|
153
|
|
|
|
|
154
|
|
|
return $this; |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
public function closeAndGetStream() |
|
158
|
|
|
{ |
|
159
|
|
|
if (!$this->isWriterOpened) { |
|
160
|
|
|
return; |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
$this->closeWriter(); |
|
164
|
|
|
|
|
165
|
|
|
$this->isWriterOpened = false; |
|
166
|
|
|
|
|
167
|
|
|
rewind($this->filePointer); |
|
168
|
|
|
|
|
169
|
|
|
return $this->filePointer; |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
/** |
|
173
|
|
|
* Checks if the pointer to the file/stream to write to is available. |
|
174
|
|
|
* Will throw an exception if not available. |
|
175
|
|
|
* |
|
176
|
|
|
* @throws IOException If the pointer is not available |
|
177
|
|
|
* @return void |
|
178
|
|
|
*/ |
|
179
|
90 |
|
protected function throwIfFilePointerIsNotAvailable() |
|
180
|
|
|
{ |
|
181
|
90 |
|
if (!$this->filePointer) { |
|
182
|
3 |
|
throw new IOException('File pointer has not be opened'); |
|
183
|
|
|
} |
|
184
|
87 |
|
} |
|
185
|
|
|
|
|
186
|
|
|
/** |
|
187
|
|
|
* Checks if the writer has already been opened, since some actions must be done before it gets opened. |
|
188
|
|
|
* Throws an exception if already opened. |
|
189
|
|
|
* |
|
190
|
|
|
* @param string $message Error message |
|
191
|
|
|
* @throws WriterAlreadyOpenedException If the writer was already opened and must not be. |
|
192
|
|
|
* @return void |
|
193
|
|
|
*/ |
|
194
|
54 |
|
protected function throwIfWriterAlreadyOpened($message) |
|
195
|
|
|
{ |
|
196
|
54 |
|
if ($this->isWriterOpened) { |
|
197
|
5 |
|
throw new WriterAlreadyOpenedException($message); |
|
198
|
|
|
} |
|
199
|
49 |
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* {@inheritdoc} |
|
203
|
|
|
*/ |
|
204
|
85 |
|
public function addRow(Row $row) |
|
205
|
|
|
{ |
|
206
|
85 |
|
if ($this->isWriterOpened) { |
|
207
|
|
|
try { |
|
208
|
75 |
|
$this->addRowToWriter($row); |
|
209
|
4 |
|
} catch (SpoutException $e) { |
|
210
|
|
|
// if an exception occurs while writing data, |
|
211
|
|
|
// close the writer and remove all files created so far. |
|
212
|
4 |
|
$this->closeAndAttemptToCleanupAllFiles(); |
|
213
|
|
|
|
|
214
|
|
|
// re-throw the exception to alert developers of the error |
|
215
|
75 |
|
throw $e; |
|
216
|
|
|
} |
|
217
|
|
|
} else { |
|
218
|
10 |
|
throw new WriterNotOpenedException('The writer needs to be opened before adding row.'); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
73 |
|
return $this; |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
/** |
|
225
|
|
|
* {@inheritdoc} |
|
226
|
|
|
*/ |
|
227
|
71 |
|
public function addRows(array $rows) |
|
228
|
|
|
{ |
|
229
|
71 |
|
foreach ($rows as $row) { |
|
230
|
71 |
|
if (!$row instanceof Row) { |
|
231
|
2 |
|
$this->closeAndAttemptToCleanupAllFiles(); |
|
232
|
2 |
|
throw new InvalidArgumentException('The input should be an array of Row'); |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
69 |
|
$this->addRow($row); |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
63 |
|
return $this; |
|
239
|
|
|
} |
|
240
|
|
|
|
|
241
|
|
|
/** |
|
242
|
|
|
* {@inheritdoc} |
|
243
|
|
|
*/ |
|
244
|
81 |
|
public function close() |
|
245
|
|
|
{ |
|
246
|
81 |
|
if (!$this->isWriterOpened) { |
|
247
|
1 |
|
return; |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
80 |
|
$this->closeWriter(); |
|
251
|
|
|
|
|
252
|
80 |
|
if (\is_resource($this->filePointer)) { |
|
253
|
80 |
|
$this->globalFunctionsHelper->fclose($this->filePointer); |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
80 |
|
$this->isWriterOpened = false; |
|
257
|
80 |
|
} |
|
258
|
|
|
|
|
259
|
|
|
/** |
|
260
|
|
|
* Closes the writer and attempts to cleanup all files that were |
|
261
|
|
|
* created during the writing process (temp files & final file). |
|
262
|
|
|
* |
|
263
|
|
|
* @return void |
|
264
|
|
|
*/ |
|
265
|
6 |
|
private function closeAndAttemptToCleanupAllFiles() |
|
266
|
|
|
{ |
|
267
|
|
|
// close the writer, which should remove all temp files |
|
268
|
6 |
|
$this->close(); |
|
269
|
|
|
|
|
270
|
|
|
// remove output file if it was created |
|
271
|
6 |
|
if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) { |
|
272
|
5 |
|
$outputFolderPath = \dirname($this->outputFilePath); |
|
273
|
5 |
|
$fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath); |
|
274
|
5 |
|
$fileSystemHelper->deleteFile($this->outputFilePath); |
|
275
|
|
|
} |
|
276
|
6 |
|
} |
|
277
|
|
|
} |
|
278
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.