Completed
Push — set_default_row_style_api ( f31a1b )
by Adrien
03:10
created

AbstractWriter::setDefaultRowStyle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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