Passed
Pull Request — develop_3.0 (#491)
by Adrien
02:47
created

WriterAbstract   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 233
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 10

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 20
lcom 2
cbo 10
dl 0
loc 233
ccs 54
cts 54
cp 1
rs 10
c 0
b 0
f 0

13 Methods

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