Failed Conditions
Push — perf-tests ( 50942d...2fc93e )
by Adrien
14:53
created

AbstractWriter::throwIfWriterAlreadyOpened()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace Box\Spout\Writer;
4
5
use Box\Spout\Common\Exception\IOException;
6
use Box\Spout\Common\Exception\InvalidArgumentException;
7
use Box\Spout\Writer\Exception\WriterAlreadyOpenedException;
8
use Box\Spout\Writer\Exception\WriterNotOpenedException;
9
use Box\Spout\Writer\Style\StyleBuilder;
10
11
/**
12
 * Class AbstractWriter
13
 *
14
 * @package Box\Spout\Writer
15
 * @abstract
16
 */
17
abstract class AbstractWriter implements WriterInterface
18
{
19
    /** @var string Path to the output file */
20
    protected $outputFilePath;
21
22
    /** @var resource Pointer to the file/stream we will write to */
23
    protected $filePointer;
24
25
    /** @var bool Indicates whether the writer has been opened or not */
26
    protected $isWriterOpened = false;
27
28
    /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */
29
    protected $globalFunctionsHelper;
30
31
    /** @var Style\Style Style to be applied to the next written row(s) */
32
    protected $rowStyle;
33
34
    /** @var Style\Style Default row style. Each writer can have its own default style */
35
    protected $defaultRowStyle;
36
37
    /** @var string Content-Type value for the header - to be defined by child class */
38
    protected static $headerContentType;
39
40
    /**
41
     * Opens the streamer and makes it ready to accept data.
42
     *
43
     * @return void
44
     * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened
45
     */
46
    abstract protected function openWriter();
47
48
    /**
49
     * Adds data to the currently openned writer.
50
     *
51
     * @param  array $dataRow Array containing data to be streamed.
52
     *          Example $dataRow = ['data1', 1234, null, '', 'data5'];
53
     * @param Style\Style $style Style to be applied to the written row
54
     * @return void
55
     */
56
    abstract protected function addRowToWriter(array $dataRow, $style);
57
58
    /**
59
     * Closes the streamer, preventing any additional writing.
60
     *
61
     * @return void
62
     */
63
    abstract protected function closeWriter();
64
65
    /**
66
     *
67
     */
68
    public function __construct()
69
    {
70
        $this->defaultRowStyle = $this->getDefaultRowStyle();
71
        $this->resetRowStyleToDefault();
72
    }
73
74
    /**
75
     * @param \Box\Spout\Common\Helper\GlobalFunctionsHelper $globalFunctionsHelper
76
     * @return AbstractWriter
77
     */
78
    public function setGlobalFunctionsHelper($globalFunctionsHelper)
79
    {
80
        $this->globalFunctionsHelper = $globalFunctionsHelper;
81
        return $this;
82
    }
83
84
    /**
85
     * Inits the writer and opens it to accept data.
86
     * By using this method, the data will be written to a file.
87
     *
88
     * @api
89
     * @param  string $outputFilePath Path of the output file that will contain the data
90
     * @return AbstractWriter
91
     * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened or if the given path is not writable
92
     */
93
    public function openToFile($outputFilePath)
94
    {
95
        $this->outputFilePath = $outputFilePath;
96
97
        $this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'wb+');
98
        $this->throwIfFilePointerIsNotAvailable();
99
100
        $this->openWriter();
101
        $this->isWriterOpened = true;
102
103
        return $this;
104
    }
105
106
    /**
107
     * Inits the writer and opens it to accept data.
108
     * By using this method, the data will be outputted directly to the browser.
109
     *
110
     * @codeCoverageIgnore
111
     *
112
     * @api
113
     * @param  string $outputFileName Name of the output file that will contain the data. If a path is passed in, only the file name will be kept
114
     * @return AbstractWriter
115
     * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened
116
     */
117
    public function openToBrowser($outputFileName)
118
    {
119
        $this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName);
120
121
        $this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w');
122
        $this->throwIfFilePointerIsNotAvailable();
123
124
        // Clear any previous output (otherwise the generated file will be corrupted)
125
        // @see https://github.com/box/spout/issues/241
126
        $this->globalFunctionsHelper->ob_end_clean();
127
128
        // Set headers
129
        $this->globalFunctionsHelper->header('Content-Type: ' . static::$headerContentType);
130
        $this->globalFunctionsHelper->header('Content-Disposition: attachment; filename="' . $this->outputFilePath . '"');
131
132
        /*
133
         * When forcing the download of a file over SSL,IE8 and lower browsers fail
134
         * if the Cache-Control and Pragma headers are not set.
135
         *
136
         * @see http://support.microsoft.com/KB/323308
137
         * @see https://github.com/liuggio/ExcelBundle/issues/45
138
         */
139
        $this->globalFunctionsHelper->header('Cache-Control: max-age=0');
140
        $this->globalFunctionsHelper->header('Pragma: public');
141
142
        $this->openWriter();
143
        $this->isWriterOpened = true;
144
145
        return $this;
146
    }
147
148
    /**
149
     * Checks if the pointer to the file/stream to write to is available.
150
     * Will throw an exception if not available.
151
     *
152
     * @return void
153
     * @throws \Box\Spout\Common\Exception\IOException If the pointer is not available
154
     */
155
    protected function throwIfFilePointerIsNotAvailable()
156
    {
157
        if (!$this->filePointer) {
158
            throw new IOException('File pointer has not be opened');
159
        }
160
    }
161
162
    /**
163
     * Checks if the writer has already been opened, since some actions must be done before it gets opened.
164
     * Throws an exception if already opened.
165
     *
166
     * @param string $message Error message
167
     * @return void
168
     * @throws \Box\Spout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened and must not be.
169
     */
170
    protected function throwIfWriterAlreadyOpened($message)
171
    {
172
        if ($this->isWriterOpened) {
173
            throw new WriterAlreadyOpenedException($message);
174
        }
175
    }
176
177
    /**
178
     * Write given data to the output. New data will be appended to end of stream.
179
     *
180
     * @param  array $dataRow Array containing data to be streamed.
181
     *                        If empty, no data is added (i.e. not even as a blank row)
182
     *                        Example: $dataRow = ['data1', 1234, null, '', 'data5', false];
183
     * @api
184
     * @return AbstractWriter
185
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
186
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
187
     */
188
    public function addRow(array $dataRow)
189
    {
190
        if ($this->isWriterOpened) {
191
            // empty $dataRow should not add an empty line
192
            if (!empty($dataRow)) {
193
                $this->addRowToWriter($dataRow, $this->rowStyle);
194
            }
195
        } else {
196
            throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
197
        }
198
199
        return $this;
200
    }
201
202
    /**
203
     * Write given data to the output and apply the given style.
204
     * @see addRow
205
     *
206
     * @api
207
     * @param array $dataRow Array of array containing data to be streamed.
208
     * @param Style\Style $style Style to be applied to the row.
209
     * @return AbstractWriter
210
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
211
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
212
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
213
     */
214
    public function addRowWithStyle(array $dataRow, $style)
215
    {
216
        if (!$style instanceof Style\Style) {
217
            throw new InvalidArgumentException('The "$style" argument must be a Style instance and cannot be NULL.');
218
        }
219
220
        $this->setRowStyle($style);
221
        $this->addRow($dataRow);
222
        $this->resetRowStyleToDefault();
223
224
        return $this;
225
    }
226
227
    /**
228
     * Write given data to the output. New data will be appended to end of stream.
229
     *
230
     * @api
231
     * @param  array $dataRows Array of array containing data to be streamed.
232
     *                         If a row is empty, it won't be added (i.e. not even as a blank row)
233
     *                         Example: $dataRows = [
234
     *                             ['data11', 12, , '', 'data13'],
235
     *                             ['data21', 'data22', null, false],
236
     *                         ];
237
     * @return AbstractWriter
238
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
239
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
240
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
241
     */
242
    public function addRows(array $dataRows)
243
    {
244
        if (!empty($dataRows)) {
245
            $firstRow = reset($dataRows);
246
            if (!is_array($firstRow)) {
247
                throw new InvalidArgumentException('The input should be an array of arrays');
248
            }
249
250
            foreach ($dataRows as $dataRow) {
251
                $this->addRow($dataRow);
252
            }
253
        }
254
255
        return $this;
256
    }
257
258
    /**
259
     * Write given data to the output and apply the given style.
260
     * @see addRows
261
     *
262
     * @api
263
     * @param array $dataRows Array of array containing data to be streamed.
264
     * @param Style\Style $style Style to be applied to the rows.
265
     * @return AbstractWriter
266
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
267
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
268
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
269
     */
270
    public function addRowsWithStyle(array $dataRows, $style)
271
    {
272
        if (!$style instanceof Style\Style) {
273
            throw new InvalidArgumentException('The "$style" argument must be a Style instance and cannot be NULL.');
274
        }
275
276
        $this->setRowStyle($style);
277
        $this->addRows($dataRows);
278
        $this->resetRowStyleToDefault();
279
280
        return $this;
281
    }
282
283
    /**
284
     * Returns the default style to be applied to rows.
285
     * Can be overriden by children to have a custom style.
286
     *
287
     * @return Style\Style
288
     */
289
    protected function getDefaultRowStyle()
290
    {
291
        return (new StyleBuilder())->build();
292
    }
293
294
    /**
295
     * Sets the style to be applied to the next written rows
296
     * until it is changed or reset.
297
     *
298
     * @param Style\Style $style
299
     * @return void
300
     */
301
    private function setRowStyle($style)
302
    {
303
        // Merge given style with the default one to inherit custom properties
304
        $this->rowStyle = $style->mergeWith($this->defaultRowStyle);
305
    }
306
307
    /**
308
     * Resets the style to be applied to the next written rows.
309
     *
310
     * @return void
311
     */
312
    private function resetRowStyleToDefault()
313
    {
314
        $this->rowStyle = $this->defaultRowStyle;
315
    }
316
317
    /**
318
     * Closes the writer. This will close the streamer as well, preventing new data
319
     * to be written to the file.
320
     *
321
     * @api
322
     * @return void
323
     */
324
    public function close()
325
    {
326
        $this->closeWriter();
327
328
        if (is_resource($this->filePointer)) {
329
            $this->globalFunctionsHelper->fclose($this->filePointer);
330
        }
331
332
        $this->isWriterOpened = false;
333
    }
334
}
335
336