Passed
Pull Request — develop_3.0 (#434)
by Adrien
03:06
created

WriterAbstract::addRowWithStyle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Box\Spout\Writer;
4
5
use Box\Spout\Common\Creator\HelperFactory;
6
use Box\Spout\Common\Exception\InvalidArgumentException;
7
use Box\Spout\Common\Exception\IOException;
8
use Box\Spout\Common\Exception\SpoutException;
9
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
10
use Box\Spout\Common\Manager\OptionsManagerInterface;
11
use Box\Spout\Writer\Common\Creator\EntityFactory;
12
use Box\Spout\Writer\Common\Entity\Cell;
13
use Box\Spout\Writer\Common\Entity\Options;
14
use Box\Spout\Writer\Common\Entity\Row;
15
use Box\Spout\Writer\Common\Entity\Style\Style;
16
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
17
use Box\Spout\Writer\Exception\WriterAlreadyOpenedException;
18
use Box\Spout\Writer\Exception\WriterNotOpenedException;
19
20
/**
21
 * Class WriterAbstract
22
 *
23
 * @abstract
24
 */
25
abstract class WriterAbstract implements WriterInterface
26
{
27
    /** @var string Path to the output file */
28
    protected $outputFilePath;
29
30
    /** @var resource Pointer to the file/stream we will write to */
31
    protected $filePointer;
32
33
    /** @var bool Indicates whether the writer has been opened or not */
34
    protected $isWriterOpened = false;
35
36
    /** @var GlobalFunctionsHelper Helper to work with global functions */
37
    protected $globalFunctionsHelper;
38
39
    /** @var HelperFactory $helperFactory */
40
    protected $helperFactory;
41
42
    /** @var OptionsManagerInterface Writer options manager */
43
    protected $optionsManager;
44
45
    /** @var StyleMerger Helps merge styles together */
46
    protected $styleMerger;
47
48
    /** @var string Content-Type value for the header - to be defined by child class */
49
    protected static $headerContentType;
50
51
    /**
52
     * @param OptionsManagerInterface $optionsManager
53
     * @param StyleMerger $styleMerger
54
     * @param GlobalFunctionsHelper $globalFunctionsHelper
55
     * @param HelperFactory $helperFactory
56
     */
57 108
    public function __construct(
58
        OptionsManagerInterface $optionsManager,
59
        StyleMerger $styleMerger,
60
        GlobalFunctionsHelper $globalFunctionsHelper,
61
        HelperFactory $helperFactory
62
    ) {
63 108
        $this->optionsManager = $optionsManager;
64 108
        $this->styleMerger = $styleMerger;
65 108
        $this->globalFunctionsHelper = $globalFunctionsHelper;
66 108
        $this->helperFactory = $helperFactory;
67 108
    }
68
69
    /**
70
     * Opens the streamer and makes it ready to accept data.
71
     *
72
     * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened
73
     * @return void
74
     */
75
    abstract protected function openWriter();
76
77
    /**
78
     * Adds a row to the currently opened writer.
79
     *
80
     * @param Row $row The row containing cells and styles
81
     * @return void
82
     */
83
    abstract protected function addRowToWriter(Row $row);
84
85
    /**
86
     * Closes the streamer, preventing any additional writing.
87
     *
88
     * @return void
89
     */
90
    abstract protected function closeWriter();
91
92
    /**
93
     * Sets the default styles for all rows added with "addRow"
94
     *
95
     * @param Style $defaultStyle
96
     * @return WriterAbstract
97
     */
98 2
    public function setDefaultRowStyle($defaultStyle)
99
    {
100 2
        $this->optionsManager->setOption(Options::DEFAULT_ROW_STYLE, $defaultStyle);
101
102 2
        return $this;
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108 97
    public function openToFile($outputFilePath)
109
    {
110 97
        $this->outputFilePath = $outputFilePath;
111
112 97
        $this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'wb+');
113 97
        $this->throwIfFilePointerIsNotAvailable();
114
115 94
        $this->openWriter();
116 94
        $this->isWriterOpened = true;
117
118 94
        return $this;
119
    }
120
121
    /**
122
     * @codeCoverageIgnore
123
     *
124
     * {@inheritdoc}
125
     */
126
    public function openToBrowser($outputFileName)
127
    {
128
        $this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName);
129
130
        $this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w');
131
        $this->throwIfFilePointerIsNotAvailable();
132
133
        // Clear any previous output (otherwise the generated file will be corrupted)
134
        // @see https://github.com/box/spout/issues/241
135
        $this->globalFunctionsHelper->ob_end_clean();
136
137
        // Set headers
138
        $this->globalFunctionsHelper->header('Content-Type: ' . static::$headerContentType);
139
        $this->globalFunctionsHelper->header('Content-Disposition: attachment; filename="' . $this->outputFilePath . '"');
140
141
        /*
142
         * When forcing the download of a file over SSL,IE8 and lower browsers fail
143
         * if the Cache-Control and Pragma headers are not set.
144
         *
145
         * @see http://support.microsoft.com/KB/323308
146
         * @see https://github.com/liuggio/ExcelBundle/issues/45
147
         */
148
        $this->globalFunctionsHelper->header('Cache-Control: max-age=0');
149
        $this->globalFunctionsHelper->header('Pragma: public');
150
151
        $this->openWriter();
152
        $this->isWriterOpened = true;
153
154
        return $this;
155
    }
156
157
    /**
158
     * Checks if the pointer to the file/stream to write to is available.
159
     * Will throw an exception if not available.
160
     *
161
     * @throws \Box\Spout\Common\Exception\IOException If the pointer is not available
162
     * @return void
163
     */
164 97
    protected function throwIfFilePointerIsNotAvailable()
165
    {
166 97
        if (!$this->filePointer) {
167 3
            throw new IOException('File pointer has not be opened');
168
        }
169 94
    }
170
171
    /**
172
     * Checks if the writer has already been opened, since some actions must be done before it gets opened.
173
     * Throws an exception if already opened.
174
     *
175
     * @param string $message Error message
176
     * @throws \Box\Spout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened and must not be.
177
     * @return void
178
     */
179 53
    protected function throwIfWriterAlreadyOpened($message)
180
    {
181 53
        if ($this->isWriterOpened) {
182 5
            throw new WriterAlreadyOpenedException($message);
183
        }
184 48
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 81
    public function addRow(Row $row)
190
    {
191 81
        if ($this->isWriterOpened) {
192 71
            if ($row->hasCells()) {
193
                try {
194 70
                    $this->applyDefaultRowStyle($row);
195 70
                    $this->addRowToWriter($row);
196 3
                } catch (SpoutException $e) {
197
                    // if an exception occurs while writing data,
198
                    // close the writer and remove all files created so far.
199 3
                    $this->closeAndAttemptToCleanupAllFiles();
200
201
                    // re-throw the exception to alert developers of the error
202 71
                    throw $e;
203
                }
204
            }
205
        } else {
206 10
            throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
207
        }
208
209 68
        return $this;
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215 63
    public function addRows(array $dataRows)
216
    {
217 63
        foreach ($dataRows as $dataRow) {
218 63
            if (!$dataRow instanceof Row) {
219 3
                $this->closeAndAttemptToCleanupAllFiles();
220 3
                throw new InvalidArgumentException();
221
            }
222
223 61
            $this->addRow($dataRow);
224
        }
225
226 52
        return $this;
227
    }
228
229
    /**
230
     * @TODO: Move this into styleMerger
231
     *
232
     * @param Row $row
233
     * @return $this
234
     */
235 70
    private function applyDefaultRowStyle(Row $row)
236
    {
237 70
        $defaultRowStyle = $this->optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
238 70
        if ($defaultRowStyle === null) {
239 8
            return $this;
240
        }
241
242 62
        $mergedStyle = $this->styleMerger->merge($row->getStyle(), $defaultRowStyle);
243 62
        $row->setStyle($mergedStyle);
244 62
    }
245
246
    /**
247
     * Closes the writer. This will close the streamer as well, preventing new data
248
     * to be written to the file.
249
     *
250
     * @return void
251
     */
252 80
    public function close()
253
    {
254 80
        if (!$this->isWriterOpened) {
255 4
            return;
256
        }
257
258 79
        $this->closeWriter();
259
260 79
        if (is_resource($this->filePointer)) {
261 79
            $this->globalFunctionsHelper->fclose($this->filePointer);
262
        }
263
264 79
        $this->isWriterOpened = false;
265 79
    }
266
267
    /**
268
     * Closes the writer and attempts to cleanup all files that were
269
     * created during the writing process (temp files & final file).
270
     *
271
     * @return void
272
     */
273 6
    private function closeAndAttemptToCleanupAllFiles()
274
    {
275
        // close the writer, which should remove all temp files
276 6
        $this->close();
277
278
        // remove output file if it was created
279 6
        if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) {
280 5
            $outputFolderPath = dirname($this->outputFilePath);
281 5
            $fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath);
282 5
            $fileSystemHelper->deleteFile($this->outputFilePath);
283
        }
284 6
    }
285
}
286