Issues (5)

src/Writer/Builder.php (1 issue)

1
<?php
2
3
/**
4
 * Class Builder.
5
 */
6
7
namespace Zhaqq\Xlsx\Writer;
8
9
use Zhaqq\Xlsx\Support;
10
use Zhaqq\Exception\XlsxException;
11
12
/**
13
 * Class Builder.
14
 */
15
class Builder
16
{
17
    use Style;
18
19
    const EXCEL_2007_MAX_ROW = 1048576;
20
21
    const EXCEL_2007_MAX_COL = 16384;
22
23
    /**
24
     * @var Sheet[]
25
     */
26
    protected $sheets = [];
27
28
    /**
29
     * @var Sheet
30
     */
31
    protected $sheet;
32
33
    /**
34
     * @var
35
     */
36
    protected $sheetName;
37
38
    /**
39
     * @var string
40
     */
41
    protected $sheetWriter = 'Xlsx';
42
43
    /**
44
     * 缓存文件列表.
45
     *
46
     * @var array
47
     */
48
    protected $tempFiles = [];
49
50
    /**
51
     * @var
52
     */
53
    protected $title = '';
54
55
    /**
56
     * @var
57
     */
58
    protected $subject = '';
59
60
    /**
61
     * @var
62
     */
63
    protected $author = '';
64
65
    /**
66
     * @var
67
     */
68
    protected $company = '';
69
70
    /**
71
     * @var
72
     */
73
    protected $description = '';
74
75
    /**
76
     * @var array
77
     */
78
    protected $keywords = [];
79
80
    protected $tempDir;
81
82
    /**
83
     * XlsxWriter constructor.
84
     */
85
    public function __construct()
86
    {
87
        if (!ini_get('date.timezone')) {
88
            date_default_timezone_set('PRC');
89
        }
90
        $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null);
91
        $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null);
92
        $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null);
93
        $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null);
94
    }
95
96
    /**
97
     * 写入sheet 不带header头 需先执行 $this->writeSheetHeader 初始化头部.
98
     *
99
     * @param \Generator|array $rows
100
     *
101
     * @example $rows = [
102
     *          'sheet_name' => 'sheet_name_1',
103
     *          'row' => [
104
     *               'title'    => 'title1',
105
     *               'content' => 'content1'
106
     *      ]
107
     * ];
108
     */
109
    public function addSheetRows($rows)
110
    {
111
        foreach ($rows as $row) {
112
            !isset($row['options']) && $row['options'] = null;
113
            $this->writeSheetRow($row['sheet_name'], $row['row'], $row['options']);
114
        }
115
    }
116
117
    /**
118
     * 写入sheet 带header头 如headers为空需先执行 $this->writeSheetHeader 初始化头部.
119
     *
120
     * @param \Generator|array $rows
121
     * @param array            $headers
122
     *
123
     * @example $rows = [
124
     *          'sheet_name' => 'sheet_name_1',
125
     *          'row' => [
126
     *               'title'    => 'title1',
127
     *               'content' => 'content1'
128
     *          ]
129
     * ];
130
     *          $headers => [
131
     *          'sheet_name' => 'sheet_name_1',
132
     *          'types' => [
133
     *               'title'    => 'string',   // 标明类型
134
     *               'content' => 'string'
135
     *          ]
136
     *
137
     * ];
138
     */
139
    public function addSheetRowsWithHeaders($rows, $headers = [])
140
    {
141
        if (!empty($headers) && is_array($headers)) {
142
            foreach ($headers as $header) {
143
                !isset($header['options']) && $header['options'] = null;
144
                $this->buildHeader($header['sheet_name'], $header['types'], $header['options'] = null);
145
            }
146
        }
147
        foreach ($rows as $row) {
148
            !isset($row['options']) && $row['options'] = null;
149
            $this->writeSheetRow($row['sheet_name'], $row['row'], $row['options']);
150
        }
151
    }
152
153
    /**
154
     * 写入文件.
155
     *
156
     * @param $filename
157
     */
158
    public function writeToFile($filename)
159
    {
160
        foreach ($this->sheets as $sheetName => $sheet) {
161
            $this->finalizeSheet($sheetName); //making sure all footers have been written
162
        }
163
164
        if (file_exists($filename)) {
165
            if (is_writable($filename)) {
166
                @unlink($filename); //if the zip already exists, remove it
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

166
                /** @scrutinizer ignore-unhandled */ @unlink($filename); //if the zip already exists, remove it

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
167
            } else {
168
                throw new XlsxException('Error in '.__CLASS__.'::'.__FUNCTION__.', file is not writeable.');
169
            }
170
        }
171
        $zip = new \ZipArchive();
172
        if (empty($this->sheets)) {
173
            throw new XlsxException('Error in '.__CLASS__.'::'.__FUNCTION__.', no worksheets defined.');
174
        }
175
        if (!$zip->open($filename, \ZipArchive::CREATE)) {
176
            throw new XlsxException('Error in '.__CLASS__.'::'.__FUNCTION__.', unable to create zip.');
177
        }
178
        $zip->addEmptyDir('docProps/');
179
        $zip->addFromString('docProps/app.xml', XlsxBuilder::buildAppXML($this->company));
180
        $zip->addFromString('docProps/core.xml', XlsxBuilder::buildCoreXML(
181
            $this->title,
182
            $this->subject,
183
            $this->author,
184
            $this->keywords,
185
            $this->description
186
        ));
187
        $zip->addEmptyDir('_rels/');
188
        $zip->addFromString('_rels/.rels', XlsxBuilder::buildRelationshipsXML());
189
        $zip->addEmptyDir('xl/worksheets/');
190
        foreach ($this->sheets as $sheet) {
191
            $zip->addFile($sheet->filename, 'xl/worksheets/'.$sheet->xmlname);
192
        }
193
        $zip->addFromString('xl/workbook.xml', XlsxBuilder::buildWorkbookXML($this->sheets));
194
        $zip->addFile($this->writeStylesXML(), 'xl/styles.xml');  //$zip->addFromString("xl/styles.xml", self::buildStylesXML() );
195
        $zip->addFromString('[Content_Types].xml', XlsxBuilder::buildContentTypesXML($this->sheets));
196
        $zip->addEmptyDir('xl/_rels/');
197
        $zip->addFromString('xl/_rels/workbook.xml.rels', XlsxBuilder::buildWorkbookRelsXML($this->sheets));
198
199
        $zip->close();
200
    }
201
202
    /**
203
     * @param       $sheetName
204
     * @param array $row
205
     * @param array $rowOptions
206
     */
207
    public function writeSheetRow($sheetName, array $row, $rowOptions = [])
208
    {
209
        if (empty($sheetName)) {
210
            return;
211
        }
212
        $this->initSheet($sheetName);
213
        $sheet = $this->getSheet($sheetName);
214
        if (count($sheet->columns) < count($row)) {
215
            $defaultColumnTypes = $this->initColumnsTypes(array_fill($from = 0, count($row), 'GENERAL')); //will map to n_auto
216
            $sheet->columns = array_merge((array) $sheet->columns, $defaultColumnTypes);
217
        }
218
219
        if (!empty($rowOptions)) {
220
            $ht = isset($rowOptions['height']) ? floatval($rowOptions['height']) : 12.1;
221
            $customHt = isset($rowOptions['height']) ? true : false;
222
            $hidden = isset($rowOptions['hidden']) ? (bool) ($rowOptions['hidden']) : false;
223
            $collapsed = isset($rowOptions['collapsed']) ? (bool) ($rowOptions['collapsed']) : false;
224
            $sheet->fileWriter->write(
225
                '<row collapsed="'.($collapsed).'" customFormat="false" customHeight="'.($customHt).
226
                '" hidden="'.($hidden).'" ht="'.($ht).'" outlineLevel="0" r="'.($sheet->rowCount + 1).'">'
227
            );
228
        } else {
229
            $sheet->fileWriter->write(
230
                '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.
231
                ($sheet->rowCount + 1).'">'
232
            );
233
        }
234
235
        $style = $rowOptions;
236
        $c = 0;
237
        foreach ($row as $v) {
238
            $numberFormat = $sheet->columns[$c]['number_format'];
239
            $numberFormatType = $sheet->columns[$c]['number_format_type'];
240
            $cellStyleIdx = empty($style) ? $sheet->columns[$c]['default_cell_style'] :
241
                $this->addCellStyle($numberFormat, json_encode(isset($style[0]) ? $style[$c] : $style));
242
            $sheet->writeCell($sheet->rowCount, $c, $v, $numberFormatType, $cellStyleIdx);
243
            ++$c;
244
        }
245
        $sheet->fileWriter->write('</row>');
246
        ++$sheet->rowCount;
247
248
        $this->sheetName = $sheetName;
249
    }
250
251
    /**
252
     * @param $sheetName
253
     */
254
    protected function finalizeSheet($sheetName)
255
    {
256
        if (empty($sheetName) || $this->sheets[$sheetName]->finalized) {
257
            return;
258
        }
259
260
        $sheet = $this->sheets[$sheetName];
261
        $sheet->finallyContent();
262
    }
263
264
    /**
265
     * @return bool|string
266
     */
267
    protected function writeStylesXML()
268
    {
269
        $r = $this->styleFontIndexes($this->cellStyles);
270
        $fills = $r['fills'];
271
        $fonts = $r['fonts'];
272
        $borders = $r['borders'];
273
        $styleIndexes = $r['styles'];
274
275
        $temporaryFilename = $this->tempFilename();
276
        $file = new XlsxWriterBuffer($temporaryFilename);
277
        $file->write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
278
        $file->write('<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
279
        $file->write('<numFmts count="'.count($this->numberFormats).'">');
280
        foreach ($this->numberFormats as $i => $v) {
281
            $file->write('<numFmt numFmtId="'.(164 + $i).'" formatCode="'.Support::xmlSpecialChars($v).'" />');
282
        }
283
        $file->write('</numFmts>');
284
        $file->write('<fonts count="'.(count($fonts)).'">');
285
        $file->write('<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
286
        $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
287
        $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
288
        $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
289
290
        foreach ($fonts as $font) {
291
            if (!empty($font)) { //fonts have 4 empty placeholders in array to offset the 4 static xml entries above
292
                $f = json_decode($font, true);
293
                $file->write('<font>');
294
                $file->write('<name val="'.htmlspecialchars($f['name']).'"/><charset val="1"/><family val="'.intval($f['family']).'"/>');
295
                $file->write('<sz val="'.intval($f['size']).'"/>');
296
                if (!empty($f['color'])) {
297
                    $file->write('<color rgb="'.strval($f['color']).'"/>');
298
                }
299
                if (!empty($f['bold'])) {
300
                    $file->write('<b val="true"/>');
301
                }
302
                if (!empty($f['italic'])) {
303
                    $file->write('<i val="true"/>');
304
                }
305
                if (!empty($f['underline'])) {
306
                    $file->write('<u val="single"/>');
307
                }
308
                if (!empty($f['strike'])) {
309
                    $file->write('<strike val="true"/>');
310
                }
311
                $file->write('</font>');
312
            }
313
        }
314
        $file->write('</fonts>');
315
316
        $file->write('<fills count="'.(count($fills)).'">');
317
        $file->write('<fill><patternFill patternType="none"/></fill>');
318
        $file->write('<fill><patternFill patternType="gray125"/></fill>');
319
        foreach ($fills as $fill) {
320
            if (!empty($fill)) { //fills have 2 empty placeholders in array to offset the 2 static xml entries above
321
                $file->write('<fill><patternFill patternType="solid"><fgColor rgb="'.strval($fill).'"/><bgColor indexed="64"/></patternFill></fill>');
322
            }
323
        }
324
        $file->write('</fills>');
325
326
        $file->write('<borders count="'.(count($borders)).'">');
327
        $file->write('<border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border>');
328
        foreach ($borders as $border) {
329
            if (!empty($border)) { //fonts have an empty placeholder in the array to offset the static xml entry above
330
                $pieces = json_decode($border, true);
331
                $border_style = !empty($pieces['style']) ? $pieces['style'] : 'hair';
332
                $border_color = !empty($pieces['color']) ? '<color rgb="'.strval($pieces['color']).'"/>' : '';
333
                $file->write('<border diagonalDown="false" diagonalUp="false">');
334
                foreach (['left', 'right', 'top', 'bottom'] as $side) {
335
                    $show_side = in_array($side, $pieces['side']) ? true : false;
336
                    $file->write($show_side ? "<$side style=\"$border_style\">$border_color</$side>" : "<$side/>");
337
                }
338
                $file->write('<diagonal/>');
339
                $file->write('</border>');
340
            }
341
        }
342
        $file->write('</borders>');
343
344
        $file->write('<cellStyleXfs count="20">');
345
        $file->write('<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
346
        $file->write('<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
347
        $file->write('<protection hidden="false" locked="true"/>');
348
        $file->write('</xf>');
349
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
350
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
351
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
352
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
353
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
354
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
355
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
356
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
357
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
358
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
359
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
360
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
361
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
362
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
363
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>');
364
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>');
365
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
366
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
367
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
368
        $file->write('</cellStyleXfs>');
369
370
        $file->write('<cellXfs count="'.(count($styleIndexes)).'">');
371
        foreach ($styleIndexes as $v) {
372
            $applyAlignment = isset($v['alignment']) ? 'true' : 'false';
373
            $wrapText = !empty($v['wrap_text']) ? 'true' : 'false';
374
            $horizAlignment = isset($v['halign']) ? $v['halign'] : 'general';
375
            $vertAlignment = isset($v['valign']) ? $v['valign'] : 'bottom';
376
            $applyBorder = isset($v['border_idx']) ? 'true' : 'false';
377
            $applyFont = 'true';
378
            $borderIdx = isset($v['border_idx']) ? intval($v['border_idx']) : 0;
379
            $fillIdx = isset($v['fill_idx']) ? intval($v['fill_idx']) : 0;
380
            $fontIdx = isset($v['font_idx']) ? intval($v['font_idx']) : 0;
381
            $file->write('<xf applyAlignment="'.$applyAlignment.'" applyBorder="'.$applyBorder.'" applyFont="'.$applyFont.'" applyProtection="false" borderId="'.($borderIdx).'" fillId="'.($fillIdx).'" fontId="'.($fontIdx).'" numFmtId="'.(164 + $v['num_fmt_idx']).'" xfId="0">');
382
            $file->write('	<alignment horizontal="'.$horizAlignment.'" vertical="'.$vertAlignment.'" textRotation="0" wrapText="'.$wrapText.'" indent="0" shrinkToFit="false"/>');
383
            $file->write('	<protection locked="true" hidden="false"/>');
384
            $file->write('</xf>');
385
        }
386
        $file->write('</cellXfs>');
387
        $file->write('<cellStyles count="6">');
388
        $file->write('<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
389
        $file->write('<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
390
        $file->write('<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>');
391
        $file->write('<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>');
392
        $file->write('<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>');
393
        $file->write('<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
394
        $file->write('</cellStyles>');
395
        $file->write('</styleSheet>');
396
        $file->close();
397
398
        return $temporaryFilename;
399
    }
400
401
    /**
402
     * @param string $sheetName
403
     * @param array  $headerTypes
404
     * @param array  $colOptions
405
     */
406
    public function buildHeader(string $sheetName, array $headerTypes, $colOptions = [])
407
    {
408
        if (empty($sheetName) || empty($headerTypes) || $this->hasSheet($sheetName)) {
409
            return;
410
        }
411
        $this->initSheet($sheetName, $colOptions, $headerTypes);
412
        $this->sheetName = $sheetName;
413
    }
414
415
    /**
416
     * @param string $sheetName
417
     * @param array  $headerTypes
418
     * @param array  $colOptions
419
     */
420
    protected function initSheet(string $sheetName, array $colOptions = [], array $headerTypes = [])
421
    {
422
        if ($this->sheetName === $sheetName || $this->hasSheet($sheetName)) {
423
            return;
424
        }
425
        $style = $colOptions;
426
        $colWidths = isset($colOptions['widths']) ? (array) $colOptions['widths'] : [];
427
        $this->createSheet($sheetName, $colOptions);
428
        $sheet = $this->getSheet($sheetName);
429
        $sheet->initContent($colWidths, $this->isTabSelected());
430
        if (!empty($headerTypes)) {
431
            $sheet->columns = $this->initColumnsTypes($headerTypes);
432
            $headerRow = array_keys($headerTypes);
433
            $writer = $sheet->getFileWriter();
434
            $writer->write(
435
                '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'. 1 .'">'
436
            );
437
            foreach ($headerRow as $c => $v) {
438
                $cellStyleIdx = empty($style) ?
439
                    $sheet->columns[$c]['default_cell_style'] :
440
                    $this->addCellStyle('GENERAL', json_encode(isset($style[0]) ? $style[$c] : $style));
441
                $sheet->writeCell(0, $c, $v, 'n_string', $cellStyleIdx);
442
            }
443
            $writer->write('</row>');
444
            ++$sheet->rowCount;
445
        }
446
447
        $this->sheetName = $sheetName;
448
    }
449
450
    protected function initColumnsTypes($headerTypes)
451
    {
452
        foreach ($headerTypes as $v) {
453
            $numberFormat = Support::numberFormatStandardized($v);
454
            $cellStyleIdx = $this->addCellStyle($numberFormat, $styleString = null);
455
            $columns[] = [
456
                'number_format' => $numberFormat,      //contains excel format like 'YYYY-MM-DD HH:MM:SS'
457
                'number_format_type' => Support::determineNumberFormatType($numberFormat), //contains friendly format like 'datetime'
458
                'default_cell_style' => $cellStyleIdx,
459
            ];
460
        }
461
462
        return $columns ?? [];
463
    }
464
465
    /**
466
     * 是否第一个sheet.
467
     *
468
     * @return bool
469
     */
470
    public function isTabSelected()
471
    {
472
        return 1 === count($this->sheets);
473
    }
474
475
    /**
476
     * @param string|null $sheetName
477
     *
478
     * @return Sheet
479
     */
480
    public function getSheet(string $sheetName = null): Sheet
481
    {
482
        if ($sheetName) {
483
            $this->sheet = $this->sheets[$sheetName];
484
        }
485
486
        return $this->sheet;
487
    }
488
489
    /**
490
     * @param Sheet $sheet
491
     */
492
    public function setSheet(Sheet $sheet)
493
    {
494
        $this->sheet = $sheet;
495
    }
496
497
    /**
498
     * @param string $string
499
     */
500
    protected function sheetWriter(string $string)
501
    {
502
        $this->sheet->fileWriter->write($string);
503
    }
504
505
    /**
506
     * @param string $sheetName
507
     * @param array  $colOptions
508
     */
509
    protected function createSheet(string $sheetName, array $colOptions = [])
510
    {
511
        $sheetFilename = $this->tempFilename();
512
        $sheetXmlName = 'sheet'.(count($this->sheets) + 1).'.xml';
513
        $autoFilter = isset($colOptions['auto_filter']) ? intval($colOptions['auto_filter']) : false;
514
        $freezeRows = isset($colOptions['freeze_rows']) ? intval($colOptions['freeze_rows']) : false;
515
        $freezeColumns = isset($colOptions['freeze_columns']) ? intval($colOptions['freeze_columns']) : false;
516
517
        $this->sheets[$sheetName] = new Sheet(
518
            [
519
                'filename' => $sheetFilename,
520
                'sheetname' => $sheetName,
521
                'xmlname' => $sheetXmlName,
522
                'row_count' => 0,
523
                'columns' => [],
524
                'merge_cells' => [],
525
                'max_cell_tag_start' => 0,
526
                'max_cell_tag_end' => 0,
527
                'auto_filter' => $autoFilter,
528
                'freeze_rows' => $freezeRows,
529
                'freeze_columns' => $freezeColumns,
530
                'finalized' => false,
531
            ],
532
            $this->sheetWriter
533
        );
534
535
        $this->sheet = $this->sheets[$sheetName];
536
    }
537
538
    /**
539
     * @param string $sheetName
540
     *
541
     * @return bool
542
     */
543
    protected function hasSheet(string $sheetName)
544
    {
545
        if (isset($this->sheets[$sheetName])) {
546
            $this->setSheet($this->sheets[$sheetName]);
547
548
            return true;
549
        }
550
551
        return false;
552
    }
553
554
    /**
555
     * @return Sheet[]
556
     */
557
    public function getSheets()
558
    {
559
        return $this->sheets;
560
    }
561
562
    /**
563
     * @param Sheet[] $sheets
564
     */
565
    public function setSheets($sheets)
566
    {
567
        $this->sheets = $sheets;
568
    }
569
570
    /**
571
     * @return bool|string
572
     */
573
    protected function tempFilename()
574
    {
575
        $tempDir = !empty($this->tempDir) ? $this->tempDir : sys_get_temp_dir();
576
        $filename = tempnam($tempDir, 'xlsx_writer_');
577
        $this->tempFiles[] = $filename;
578
579
        return $filename;
580
    }
581
582
    /**
583
     * @param string $title
584
     *
585
     * @return $this
586
     */
587
    public function setTitle($title = '')
588
    {
589
        $this->title = $title;
590
591
        return $this;
592
    }
593
594
    /**
595
     * @param string $subject
596
     *
597
     * @return $this
598
     */
599
    public function setSubject($subject = '')
600
    {
601
        $this->subject = $subject;
602
603
        return $this;
604
    }
605
606
    /**
607
     * @param string $author
608
     *
609
     * @return $this
610
     */
611
    public function setAuthor($author = '')
612
    {
613
        $this->author = $author;
614
615
        return $this;
616
    }
617
618
    /**
619
     * @param string $company
620
     *
621
     * @return $this
622
     */
623
    public function setCompany($company = '')
624
    {
625
        $this->company = $company;
626
627
        return $this;
628
    }
629
630
    /**
631
     * @param array $keywords
632
     *
633
     * @return $this
634
     */
635
    public function setKeywords(array $keywords = [])
636
    {
637
        $this->keywords = $keywords;
638
639
        return $this;
640
    }
641
642
    /**
643
     * @param string $description
644
     *
645
     * @return $this
646
     */
647
    public function setDescription($description = '')
648
    {
649
        $this->description = $description;
650
651
        return $this;
652
    }
653
654
    public function __destruct()
655
    {
656
        if (!empty($this->tempFiles)) {
657
            foreach ($this->tempFiles as $tempFile) {
658
                /* @scrutinizer ignore-unhandled */
659
                @unlink($tempFile);
660
            }
661
        }
662
    }
663
}
664