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

FileSystemHelper   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 21
c 6
b 1
f 1
lcom 1
cbo 5
dl 0
loc 357
rs 10

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getRootFolder() 0 4 1
A getXlFolder() 0 4 1
A getXlWorksheetsFolder() 0 4 1
A createBaseFilesAndFolders() 0 8 1
A createRootFolder() 0 5 1
A createRelsFolderAndFile() 0 8 1
A createRelsFile() 0 15 1
A createDocPropsFolderAndFiles() 0 9 1
A createAppXmlFile() 0 15 1
A createCoreXmlFile() 0 16 1
A createXlFolderAndSubFolders() 0 8 1
A createXlRelsFolder() 0 5 1
A createXlWorksheetsFolder() 0 5 1
B createContentTypesFile() 0 27 2
B createWorkbookFile() 0 27 2
A createWorkbookRelsFile() 0 21 2
A createStylesFile() 0 7 1
A zipRootFolderAndCopyToStream() 0 17 1
1
<?php
2
3
namespace Box\Spout\Writer\XLSX\Helper;
4
5
use Box\Spout\Writer\Common\Helper\ZipHelper;
6
use Box\Spout\Writer\XLSX\Internal\Worksheet;
7
8
/**
9
 * Class FileSystemHelper
10
 * This class provides helper functions to help with the file system operations
11
 * like files/folders creation & deletion for XLSX files
12
 *
13
 * @package Box\Spout\Writer\XLSX\Helper
14
 */
15
class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
16
{
17
    const APP_NAME = 'Spout';
18
19
    const RELS_FOLDER_NAME = '_rels';
20
    const DOC_PROPS_FOLDER_NAME = 'docProps';
21
    const XL_FOLDER_NAME = 'xl';
22
    const WORKSHEETS_FOLDER_NAME = 'worksheets';
23
24
    const RELS_FILE_NAME = '.rels';
25
    const APP_XML_FILE_NAME = 'app.xml';
26
    const CORE_XML_FILE_NAME = 'core.xml';
27
    const CONTENT_TYPES_XML_FILE_NAME = '[Content_Types].xml';
28
    const WORKBOOK_XML_FILE_NAME = 'workbook.xml';
29
    const WORKBOOK_RELS_XML_FILE_NAME = 'workbook.xml.rels';
30
    const STYLES_XML_FILE_NAME = 'styles.xml';
31
32
    /** @var string Path to the root folder inside the temp folder where the files to create the XLSX will be stored */
33
    protected $rootFolder;
34
35
    /** @var string Path to the "_rels" folder inside the root folder */
36
    protected $relsFolder;
37
38
    /** @var string Path to the "docProps" folder inside the root folder */
39
    protected $docPropsFolder;
40
41
    /** @var string Path to the "xl" folder inside the root folder */
42
    protected $xlFolder;
43
44
    /** @var string Path to the "_rels" folder inside the "xl" folder */
45
    protected $xlRelsFolder;
46
47
    /** @var string Path to the "worksheets" folder inside the "xl" folder */
48
    protected $xlWorksheetsFolder;
49
50
    /**
51
     * @return string
52
     */
53
    public function getRootFolder()
54
    {
55
        return $this->rootFolder;
56
    }
57
58
    /**
59
     * @return string
60
     */
61
    public function getXlFolder()
62
    {
63
        return $this->xlFolder;
64
    }
65
66
    /**
67
     * @return string
68
     */
69
    public function getXlWorksheetsFolder()
70
    {
71
        return $this->xlWorksheetsFolder;
72
    }
73
74
    /**
75
     * Creates all the folders needed to create a XLSX file, as well as the files that won't change.
76
     *
77
     * @return void
78
     * @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders
79
     */
80
    public function createBaseFilesAndFolders()
81
    {
82
        $this
83
            ->createRootFolder()
84
            ->createRelsFolderAndFile()
85
            ->createDocPropsFolderAndFiles()
86
            ->createXlFolderAndSubFolders();
87
    }
88
89
    /**
90
     * Creates the folder that will be used as root
91
     *
92
     * @return FileSystemHelper
93
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
94
     */
95
    protected function createRootFolder()
96
    {
97
        $this->rootFolder = $this->createFolder($this->baseFolderPath, uniqid('xlsx', true));
98
        return $this;
99
    }
100
101
    /**
102
     * Creates the "_rels" folder under the root folder as well as the ".rels" file in it
103
     *
104
     * @return FileSystemHelper
105
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or the ".rels" file
106
     */
107
    protected function createRelsFolderAndFile()
108
    {
109
        $this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME);
110
111
        $this->createRelsFile();
112
113
        return $this;
114
    }
115
116
    /**
117
     * Creates the ".rels" file under the "_rels" folder (under root)
118
     *
119
     * @return FileSystemHelper
120
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
121
     */
122
    protected function createRelsFile()
123
    {
124
        $relsFileContents = <<<EOD
125
<?xml version="1.0" encoding="UTF-8"?>
126
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
127
    <Relationship Id="rIdWorkbook" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
128
    <Relationship Id="rIdCore" Type="http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
129
    <Relationship Id="rIdApp" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
130
</Relationships>
131
EOD;
132
133
        $this->createFileWithContents($this->relsFolder, self::RELS_FILE_NAME, $relsFileContents);
134
135
        return $this;
136
    }
137
138
    /**
139
     * Creates the "docProps" folder under the root folder as well as the "app.xml" and "core.xml" files in it
140
     *
141
     * @return FileSystemHelper
142
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or one of the files
143
     */
144
    protected function createDocPropsFolderAndFiles()
145
    {
146
        $this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME);
147
148
        $this->createAppXmlFile();
149
        $this->createCoreXmlFile();
150
151
        return $this;
152
    }
153
154
    /**
155
     * Creates the "app.xml" file under the "docProps" folder
156
     *
157
     * @return FileSystemHelper
158
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
159
     */
160
    protected function createAppXmlFile()
161
    {
162
        $appName = self::APP_NAME;
163
        $appXmlFileContents = <<<EOD
164
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
165
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
166
    <Application>$appName</Application>
167
    <TotalTime>0</TotalTime>
168
</Properties>
169
EOD;
170
171
        $this->createFileWithContents($this->docPropsFolder, self::APP_XML_FILE_NAME, $appXmlFileContents);
172
173
        return $this;
174
    }
175
176
    /**
177
     * Creates the "core.xml" file under the "docProps" folder
178
     *
179
     * @return FileSystemHelper
180
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
181
     */
182
    protected function createCoreXmlFile()
183
    {
184
        $createdDate = (new \DateTime())->format(\DateTime::W3C);
185
        $coreXmlFileContents = <<<EOD
186
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
187
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
188
    <dcterms:created xsi:type="dcterms:W3CDTF">$createdDate</dcterms:created>
189
    <dcterms:modified xsi:type="dcterms:W3CDTF">$createdDate</dcterms:modified>
190
    <cp:revision>0</cp:revision>
191
</cp:coreProperties>
192
EOD;
193
194
        $this->createFileWithContents($this->docPropsFolder, self::CORE_XML_FILE_NAME, $coreXmlFileContents);
195
196
        return $this;
197
    }
198
199
    /**
200
     * Creates the "xl" folder under the root folder as well as its subfolders
201
     *
202
     * @return FileSystemHelper
203
     * @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the folders
204
     */
205
    protected function createXlFolderAndSubFolders()
206
    {
207
        $this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
208
        $this->createXlRelsFolder();
209
        $this->createXlWorksheetsFolder();
210
211
        return $this;
212
    }
213
214
    /**
215
     * Creates the "_rels" folder under the "xl" folder
216
     *
217
     * @return FileSystemHelper
218
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
219
     */
220
    protected function createXlRelsFolder()
221
    {
222
        $this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME);
223
        return $this;
224
    }
225
226
    /**
227
     * Creates the "worksheets" folder under the "xl" folder
228
     *
229
     * @return FileSystemHelper
230
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
231
     */
232
    protected function createXlWorksheetsFolder()
233
    {
234
        $this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME);
235
        return $this;
236
    }
237
238
    /**
239
     * Creates the "[Content_Types].xml" file under the root folder
240
     *
241
     * @param Worksheet[] $worksheets
242
     * @return FileSystemHelper
243
     */
244
    public function createContentTypesFile($worksheets)
245
    {
246
        $contentTypesXmlFileContents = <<<EOD
247
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
248
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
249
    <Default ContentType="application/xml" Extension="xml"/>
250
    <Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
251
    <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>
252
EOD;
253
254
    /** @var Worksheet $worksheet */
255
    foreach ($worksheets as $worksheet) {
256
        $contentTypesXmlFileContents .= '<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" PartName="/xl/worksheets/sheet' . $worksheet->getId() . '.xml"/>';
257
    }
258
259
    $contentTypesXmlFileContents .= <<<EOD
260
    <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" PartName="/xl/styles.xml"/>
261
    <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" PartName="/xl/sharedStrings.xml"/>
262
    <Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>
263
    <Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>
264
</Types>
265
EOD;
266
267
        $this->createFileWithContents($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME, $contentTypesXmlFileContents);
268
269
        return $this;
270
    }
271
272
    /**
273
     * Creates the "workbook.xml" file under the "xl" folder
274
     *
275
     * @param Worksheet[] $worksheets
276
     * @return FileSystemHelper
277
     */
278
    public function createWorkbookFile($worksheets)
279
    {
280
        $workbookXmlFileContents = <<<EOD
281
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
282
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
283
    <sheets>
284
EOD;
285
286
        /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
287
        $escaper = \Box\Spout\Common\Escaper\XLSX::getInstance();
288
289
        /** @var Worksheet $worksheet */
290
        foreach ($worksheets as $worksheet) {
291
            $worksheetName = $worksheet->getExternalSheet()->getName();
292
            $worksheetId = $worksheet->getId();
293
            $workbookXmlFileContents .= '<sheet name="' . $escaper->escape($worksheetName) . '" sheetId="' . $worksheetId . '" r:id="rIdSheet' . $worksheetId . '"/>';
294
        }
295
296
        $workbookXmlFileContents .= <<<EOD
297
    </sheets>
298
</workbook>
299
EOD;
300
301
        $this->createFileWithContents($this->xlFolder, self::WORKBOOK_XML_FILE_NAME, $workbookXmlFileContents);
302
303
        return $this;
304
    }
305
306
    /**
307
     * Creates the "workbook.xml.res" file under the "xl/_res" folder
308
     *
309
     * @param Worksheet[] $worksheets
310
     * @return FileSystemHelper
311
     */
312
    public function createWorkbookRelsFile($worksheets)
313
    {
314
        $workbookRelsXmlFileContents = <<<EOD
315
<?xml version="1.0" encoding="UTF-8"?>
316
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
317
    <Relationship Id="rIdStyles" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
318
    <Relationship Id="rIdSharedStrings" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
319
EOD;
320
321
        /** @var Worksheet $worksheet */
322
        foreach ($worksheets as $worksheet) {
323
            $worksheetId = $worksheet->getId();
324
            $workbookRelsXmlFileContents .= '<Relationship Id="rIdSheet' . $worksheetId . '" Target="worksheets/sheet' . $worksheetId . '.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"/>';
325
        }
326
327
        $workbookRelsXmlFileContents .= '</Relationships>';
328
329
        $this->createFileWithContents($this->xlRelsFolder, self::WORKBOOK_RELS_XML_FILE_NAME, $workbookRelsXmlFileContents);
330
331
        return $this;
332
    }
333
334
    /**
335
     * Creates the "styles.xml" file under the "xl" folder
336
     *
337
     * @param StyleHelper $styleHelper
338
     * @return FileSystemHelper
339
     */
340
    public function createStylesFile($styleHelper)
341
    {
342
        $stylesXmlFileContents = $styleHelper->getStylesXMLFileContent();
343
        $this->createFileWithContents($this->xlFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
344
345
        return $this;
346
    }
347
348
    /**
349
     * Zips the root folder and streams the contents of the zip into the given stream
350
     *
351
     * @param resource $streamPointer Pointer to the stream to copy the zip
352
     * @return void
353
     */
354
    public function zipRootFolderAndCopyToStream($streamPointer)
355
    {
356
        $zipHelper = new ZipHelper($this->rootFolder);
357
358
        // In order to have the file's mime type detected properly, files need to be added
359
        // to the zip file in a particular order.
360
        // "[Content_Types].xml" then at least 2 files located in "xl" folder should be zipped first.
361
        $zipHelper->addFileToArchive($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME);
362
        $zipHelper->addFileToArchive($this->rootFolder, self::XL_FOLDER_NAME . '/' . self::WORKBOOK_XML_FILE_NAME);
363
        $zipHelper->addFileToArchive($this->rootFolder, self::XL_FOLDER_NAME . '/' . self::STYLES_XML_FILE_NAME);
364
365
        $zipHelper->addFolderToArchive($this->rootFolder, ZipHelper::EXISTING_FILES_SKIP);
366
        $zipHelper->closeArchiveAndCopyToStream($streamPointer);
367
368
        // once the zip is copied, remove it
369
        $this->deleteFile($zipHelper->getZipFilePath());
370
    }
371
}
372