Completed
Push — set_default_row_style_api ( f31a1b...3e085f )
by Adrien
02:47
created

AbstractWriter::throwIfFilePointerIsNotAvailable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 4
cp 1
cc 2
eloc 3
nc 2
nop 0
crap 2
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 288
    public function __construct()
69
    {
70 288
        $this->defaultRowStyle = $this->getDefaultRowStyle();
71 288
        $this->resetRowStyleToDefault();
72 288
    }
73
74
    /**
75
     * Sets the default styles for all rows added with "addRow".
76
     * Overriding the default style instead of using "addRowWithStyle" improves performance by 20%.
77
     * @see https://github.com/box/spout/issues/272
78
     *
79
     * @param Style\Style $defaultStyle
80
     * @return AbstractWriter
81
     */
82 6
    public function setDefaultRowStyle($defaultStyle)
83
    {
84 6
        $this->defaultRowStyle = $defaultStyle;
85 6
        $this->resetRowStyleToDefault();
86 6
        return $this;
87
    }
88
89
    /**
90
     * @param \Box\Spout\Common\Helper\GlobalFunctionsHelper $globalFunctionsHelper
91
     * @return AbstractWriter
92
     */
93 288
    public function setGlobalFunctionsHelper($globalFunctionsHelper)
94
    {
95 288
        $this->globalFunctionsHelper = $globalFunctionsHelper;
96 288
        return $this;
97
    }
98
99
    /**
100
     * Inits the writer and opens it to accept data.
101
     * By using this method, the data will be written to a file.
102
     *
103
     * @api
104
     * @param  string $outputFilePath Path of the output file that will contain the data
105
     * @return AbstractWriter
106
     * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened or if the given path is not writable
107
     */
108 255
    public function openToFile($outputFilePath)
109
    {
110 255
        $this->outputFilePath = $outputFilePath;
111
112 255
        $this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'wb+');
1 ignored issue
show
Documentation Bug introduced by
It seems like $this->globalFunctionsHe...>outputFilePath, 'wb+') can also be of type boolean. However, the property $filePointer is declared as type resource. Maybe add an additional type check?

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 $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
113 255
        $this->throwIfFilePointerIsNotAvailable();
114
115 246
        $this->openWriter();
116 246
        $this->isWriterOpened = true;
117
118 246
        return $this;
119
    }
120
121
    /**
122
     * Inits the writer and opens it to accept data.
123
     * By using this method, the data will be outputted directly to the browser.
124
     *
125
     * @codeCoverageIgnore
126
     *
127
     * @api
128
     * @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
129
     * @return AbstractWriter
130
     * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened
131
     */
132
    public function openToBrowser($outputFileName)
133
    {
134
        $this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName);
135
136
        $this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w');
1 ignored issue
show
Documentation Bug introduced by
It seems like $this->globalFunctionsHe...en('php://output', 'w') can also be of type boolean. However, the property $filePointer is declared as type resource. Maybe add an additional type check?

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 $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
137
        $this->throwIfFilePointerIsNotAvailable();
138
139
        // Clear any previous output (otherwise the generated file will be corrupted)
140
        // @see https://github.com/box/spout/issues/241
141
        $this->globalFunctionsHelper->ob_end_clean();
142
143
        // Set headers
144
        $this->globalFunctionsHelper->header('Content-Type: ' . static::$headerContentType);
145
        $this->globalFunctionsHelper->header('Content-Disposition: attachment; filename="' . $this->outputFilePath . '"');
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
     * @return void
168
     * @throws \Box\Spout\Common\Exception\IOException If the pointer is not available
169
     */
170 255
    protected function throwIfFilePointerIsNotAvailable()
171
    {
172 255
        if (!$this->filePointer) {
173 9
            throw new IOException('File pointer has not be opened');
174
        }
175 246
    }
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
     * @return void
183
     * @throws \Box\Spout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened and must not be.
184
     */
185 123
    protected function throwIfWriterAlreadyOpened($message)
186
    {
187 123
        if ($this->isWriterOpened) {
188 15
            throw new WriterAlreadyOpenedException($message);
189
        }
190 108
    }
191
192
    /**
193
     * Write given data to the output. New data will be appended to end of stream.
194
     *
195
     * @param  array $dataRow Array containing data to be streamed.
196
     *                        If empty, no data is added (i.e. not even as a blank row)
197
     *                        Example: $dataRow = ['data1', 1234, null, '', 'data5', false];
198
     * @api
199
     * @return AbstractWriter
200
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
201
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
202
     */
203 207
    public function addRow(array $dataRow)
204
    {
205 207
        if ($this->isWriterOpened) {
206
            // empty $dataRow should not add an empty line
207 177
            if (!empty($dataRow)) {
208 177
                $this->addRowToWriter($dataRow, $this->rowStyle);
209 171
            }
210 171
        } else {
211 30
            throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
212
        }
213
214 171
        return $this;
215
    }
216
217
    /**
218
     * Write given data to the output and apply the given style.
219
     * @see addRow
220
     *
221
     * @api
222
     * @param array $dataRow Array of array containing data to be streamed.
223
     * @param Style\Style $style Style to be applied to the row.
224
     * @return AbstractWriter
225
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
226
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
227
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
228
     */
229 48
    public function addRowWithStyle(array $dataRow, $style)
230
    {
231 48
        if (!$style instanceof Style\Style) {
232 18
            throw new InvalidArgumentException('The "$style" argument must be a Style instance and cannot be NULL.');
233
        }
234
235 30
        $this->setRowStyle($style);
236 30
        $this->addRow($dataRow);
237 18
        $this->resetRowStyleToDefault();
238
239 18
        return $this;
240
    }
241
242
    /**
243
     * Write given data to the output. New data will be appended to end of stream.
244
     *
245
     * @api
246
     * @param  array $dataRows Array of array containing data to be streamed.
247
     *                         If a row is empty, it won't be added (i.e. not even as a blank row)
248
     *                         Example: $dataRows = [
249
     *                             ['data11', 12, , '', 'data13'],
250
     *                             ['data21', 'data22', null, false],
251
     *                         ];
252
     * @return AbstractWriter
253
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
254
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
255
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
256
     */
257 153
    public function addRows(array $dataRows)
258
    {
259 153
        if (!empty($dataRows)) {
260 153
            $firstRow = reset($dataRows);
261 153
            if (!is_array($firstRow)) {
262 3
                throw new InvalidArgumentException('The input should be an array of arrays');
263
            }
264
265 150
            foreach ($dataRows as $dataRow) {
266 150
                $this->addRow($dataRow);
267 135
            }
268 135
        }
269
270 135
        return $this;
271
    }
272
273
    /**
274
     * Write given data to the output and apply the given style.
275
     * @see addRows
276
     *
277
     * @api
278
     * @param array $dataRows Array of array containing data to be streamed.
279
     * @param Style\Style $style Style to be applied to the rows.
280
     * @return AbstractWriter
281
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
282
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
283
     * @throws \Box\Spout\Common\Exception\IOException If unable to write data
284
     */
285 48
    public function addRowsWithStyle(array $dataRows, $style)
286
    {
287 48
        if (!$style instanceof Style\Style) {
288 18
            throw new InvalidArgumentException('The "$style" argument must be a Style instance and cannot be NULL.');
289
        }
290
291 30
        $this->setRowStyle($style);
292 30
        $this->addRows($dataRows);
293 30
        $this->resetRowStyleToDefault();
294
295 30
        return $this;
296
    }
297
298
    /**
299
     * Returns the default style to be applied to rows.
300
     * Can be overriden by children to have a custom style.
301
     *
302
     * @return Style\Style
303
     */
304 162
    protected function getDefaultRowStyle()
305
    {
306 162
        return (new StyleBuilder())->build();
307
    }
308
309
    /**
310
     * Sets the style to be applied to the next written rows
311
     * until it is changed or reset.
312
     *
313
     * @param Style\Style $style
314
     * @return void
315
     */
316 60
    private function setRowStyle($style)
317
    {
318
        // Merge given style with the default one to inherit custom properties
319 60
        $this->rowStyle = $style->mergeWith($this->defaultRowStyle);
320 60
    }
321
322
    /**
323
     * Resets the style to be applied to the next written rows.
324
     *
325
     * @return void
326
     */
327 288
    private function resetRowStyleToDefault()
328
    {
329 288
        $this->rowStyle = $this->defaultRowStyle;
330 288
    }
331
332
    /**
333
     * Closes the writer. This will close the streamer as well, preventing new data
334
     * to be written to the file.
335
     *
336
     * @api
337
     * @return void
338
     */
339 183
    public function close()
340
    {
341 183
        $this->closeWriter();
342
343 183
        if (is_resource($this->filePointer)) {
344 183
            $this->globalFunctionsHelper->fclose($this->filePointer);
345 183
        }
346
347 183
        $this->isWriterOpened = false;
348 183
    }
349
}
350
351