GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 27d56f...c341c3 )
by Denis
03:01
created

ExcelWriter   D

Complexity

Total Complexity 127

Size/Duplication

Total Lines 714
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 4
Bugs 3 Features 0
Metric Value
wmc 127
c 4
b 3
f 0
lcom 1
cbo 9
dl 0
loc 714
rs 4.4444

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 3
A setAuthor() 0 4 1
A __destruct() 0 10 4
A setTmpDir() 0 4 1
A tempFilename() 0 8 2
A writeToStdOut() 0 6 1
A writeToString() 0 8 1
C writeToFile() 0 49 8
B initializeSheet() 0 45 4
C determineCellType() 0 27 11
C escapeCellFormat() 0 29 14
A addCellFormat() 0 12 2
C getCellFormat() 0 37 11
B writeSheetHeader() 0 18 6
A writeRowHeader() 0 11 2
B writeSheetRow() 0 32 5
B finalizeSheet() 0 45 5
A markMergedCell() 0 12 3
B writeSheet() 0 12 5
C writeCell() 0 47 14
A writeStylesXML() 0 10 1
A setSharedString() 0 12 2
A writeSharedStringsXML() 0 10 1
A xlsCell() 0 9 2
A log() 0 7 2
A checkFilename() 0 9 1
A xmlspecialchars() 0 4 1
C convertDateTime() 0 38 14

How to fix   Complexity   

Complex Class

Complex classes like ExcelWriter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ExcelWriter, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Ellumilel;
3
4
use Ellumilel\DocProps\App;
5
use Ellumilel\DocProps\Core;
6
use Ellumilel\Rels\Relationships;
7
use Ellumilel\Xl\SharedStrings;
8
use Ellumilel\Xl\Styles;
9
use Ellumilel\Xl\Workbook;
10
11
/**
12
 * Class ExcelWriter
13
 * @package Ellumilel
14
 * @author Denis Tikhonov <[email protected]>
15
 */
16
class ExcelWriter
17
{
18
    /**
19
     * @link http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx
20
     */
21
    const EXCEL_MAX_ROW = 1048576;
22
    const EXCEL_MAX_RANGE = 2147483647;
23
    const EXCEL_MAX_COL = 16384;
24
25
    /** @var string */
26
    private $urlSchemaFormat = 'http://schemas.openxmlformats.org/officeDocument/2006';
27
28
    /** @var string */
29
    protected $author ='Unknown Author';
30
    /** @var array */
31
    protected $sheets = [];
32
    /** @var array */
33
    protected $sharedStrings = [];//unique set
34
    /** @var int */
35
    protected $sharedStringCount = 0;//count of non-unique references to the unique set
36
    /** @var array */
37
    protected $tempFiles = [];
38
    /** @var array */
39
    protected $cellFormats = [];//contains excel format like YYYY-MM-DD HH:MM:SS
40
    /** @var array */
41
    protected $cellTypes = [];//contains friendly format like datetime
42
    /** @var string  */
43
    protected $currentSheet = '';
44
    /** @var null */
45
    protected $tmpDir = null;
46
    /** @var Core */
47
    protected $core;
48
    /** @var App */
49
    protected $app;
50
    /** @var Workbook */
51
    protected $workbook;
52
53
    /**
54
     * ExcelWriter constructor.
55
     * @throws \Exception
56
     */
57
    public function __construct()
58
    {
59
        if (!class_exists('ZipArchive')) {
60
            throw new \Exception('ZipArchive not found');
61
        }
62
63
        if (!ini_get('date.timezone')) {
64
            //using date functions can kick out warning if this isn't set
65
            date_default_timezone_set('UTC');
66
        }
67
        $this->addCellFormat($cell_format = 'GENERAL');
68
        $this->core = new Core();
69
        $this->app = new App();
70
        $this->workbook = new Workbook();
71
    }
72
73
    /**
74
     * @param string $author
75
     */
76
    public function setAuthor($author = '')
77
    {
78
        $this->author = $author;
79
    }
80
81
    public function __destruct()
82
    {
83
        if (!empty($this->tempFiles)) {
84
            foreach ($this->tempFiles as $tempFile) {
85
                if (file_exists($tempFile)) {
86
                    unlink($tempFile);
87
                }
88
            }
89
        }
90
    }
91
92
    /**
93
     * @param $dir
94
     */
95
    public function setTmpDir($dir)
96
    {
97
        $this->tmpDir = $dir;
98
    }
99
100
    /**
101
     * Return tmpFileName
102
     * @return string
103
     */
104
    protected function tempFilename()
105
    {
106
        $tmpDir = is_null($this->tmpDir) ? sys_get_temp_dir() : $this->tmpDir;
107
        $filename = tempnam($tmpDir, "exlsWriter_");
108
        $this->tempFiles[] = $filename;
109
110
        return $filename;
111
    }
112
113
    public function writeToStdOut()
114
    {
115
        $tempFile = $this->tempFilename();
116
        $this->writeToFile($tempFile);
117
        readfile($tempFile);
118
    }
119
120
    /**
121
     * @return string
122
     */
123
    public function writeToString()
124
    {
125
        $tempFile = $this->tempFilename();
126
        $this->writeToFile($tempFile);
127
        $string = file_get_contents($tempFile);
128
129
        return $string;
130
    }
131
132
    /**
133
     * @param string $filename
134
     */
135
    public function writeToFile($filename)
136
    {
137
        $zip = new \ZipArchive();
138
        foreach ($this->sheets as $sheetName => $sheet) {
139
            $this->finalizeSheet($sheetName);
140
        }
141
        if (file_exists($filename) && is_writable($filename)) {
142
            unlink($filename);
143
        }
144
145
        if (empty($this->sheets) || !$zip->open($filename, \ZipArchive::CREATE)) {
146
            self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined or unable to create zip.");
147
            return;
148
        }
149
150
        $this->workbook->setSheet($this->sheets);
151
152
        $contentTypes = new ContentTypes(!empty($this->sharedStrings));
153
        $contentTypes->setSheet($this->sheets);
154
155
        $rels = new Relationships(!empty($this->sharedStrings));
156
        $rels->setSheet($this->sheets);
157
158
        $zip->addEmptyDir("docProps/");
159
        $zip->addFromString("docProps/app.xml", $this->app->buildAppXML());
160
        $zip->addFromString("docProps/core.xml", $this->core->buildCoreXML());
161
        $zip->addEmptyDir("_rels/");
162
        $zip->addFromString("_rels/.rels", $rels->buildRelationshipsXML());
163
        $zip->addEmptyDir("xl/worksheets/");
164
        foreach ($this->sheets as $sheet) {
165
            /** @var Sheet $sheet */
166
            $zip->addFile($sheet->getFilename(), "xl/worksheets/".$sheet->getXmlName());
167
        }
168
        if (!empty($this->sharedStrings)) {
169
            $zip->addFile(
170
                $this->writeSharedStringsXML(),
171
                "xl/sharedStrings.xml"
172
            );
173
        }
174
        $zip->addFromString("xl/workbook.xml", $this->workbook->buildWorkbookXML());
175
        $zip->addFile(
176
            $this->writeStylesXML(),
177
            "xl/styles.xml"
178
        );
179
        $zip->addFromString("[Content_Types].xml", $contentTypes->buildContentTypesXML());
180
        $zip->addEmptyDir("xl/_rels/");
181
        $zip->addFromString("xl/_rels/workbook.xml.rels", $rels->buildWorkbookRelationshipsXML());
182
        $zip->close();
183
    }
184
185
    /**
186
     * @param string $sheetName
187
     */
188
    protected function initializeSheet($sheetName)
189
    {
190
        if ($this->currentSheet == $sheetName || isset($this->sheets[$sheetName])) {
191
            return;
192
        }
193
        $sheetFilename = $this->tempFilename();
194
        $sheetXmlName = 'sheet' . (count($this->sheets) + 1).".xml";
195
        $sheetObj = new Sheet();
196
        $sheetObj
197
            ->setFilename($sheetFilename)
198
            ->setSheetName($sheetName)
199
            ->setXmlName($sheetXmlName)
200
            ->setWriter(new Writer($sheetFilename))
201
        ;
202
        $this->sheets[$sheetName] = $sheetObj;
203
        /** @var Sheet $sheet */
204
        $sheet = &$this->sheets[$sheetName];
205
        $selectedTab = count($this->sheets) == 1 ? 'true' : 'false';//only first sheet is selected
206
        $maxCell = ExcelWriter::xlsCell(self::EXCEL_MAX_ROW, self::EXCEL_MAX_COL);//XFE1048577
207
        $sheet->getWriter()->write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
208
        $sheet->getWriter()->write(
209
            '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" 
210
                xmlns:r="'.$this->urlSchemaFormat.'/relationships">'
211
        );
212
        $sheet->getWriter()->write('<sheetPr filterMode="false">');
213
        $sheet->getWriter()->write('<pageSetUpPr fitToPage="false"/>');
214
        $sheet->getWriter()->write('</sheetPr>');
215
        $sheet->setMaxCellTagStart($sheet->getWriter()->fTell());
216
        $sheet->getWriter()->write('<dimension ref="A1:'.$maxCell.'"/>');
217
        $sheet->setMaxCellTagEnd($sheet->getWriter()->fTell());
218
        $sheet->getWriter()->write('<sheetViews>');
219
        $sheet->getWriter()->write(
220
            '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" 
221
            showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" 
222
            tabSelected="'.$selectedTab.'" topLeftCell="A1" view="normal" windowProtection="false" 
223
            workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">'
224
        );
225
        $sheet->getWriter()->write('<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
226
        $sheet->getWriter()->write('</sheetView>');
227
        $sheet->getWriter()->write('</sheetViews>');
228
        $sheet->getWriter()->write('<cols>');
229
        $sheet->getWriter()->write('<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="11.5"/>');
230
        $sheet->getWriter()->write('</cols>');
231
        $sheet->getWriter()->write('<sheetData>');
232
    }
233
234
    /**
235
     * @param $cellFormat
236
     *
237
     * @return string
238
     */
239
    private function determineCellType($cellFormat)
240
    {
241
        $cellFormat = str_replace("[RED]", "", $cellFormat);
242
        if ($cellFormat == 'GENERAL') {
243
            return 'string';
244
        }
245
        if ($cellFormat == '0' || preg_match("/0/", $cellFormat)) {
246
            return 'numeric';
247
        }
248
        if (preg_match("/[H]{1,2}:[M]{1,2}/", $cellFormat) || preg_match("/[M]{1,2}:[S]{1,2}/", $cellFormat)) {
249
            return 'datetime';
250
        }
251
        if (preg_match("/[YY]{2,4}/", $cellFormat)
252
            || preg_match("/[D]{1,2}/", $cellFormat)
253
            || preg_match("/[M]{1,2}/", $cellFormat)
254
        ) {
255
            return 'date';
256
        }
257
        if (preg_match("/$/", $cellFormat)) {
258
            return 'currency';
259
        }
260
        if (preg_match("/%/", $cellFormat)) {
261
            return 'percent';
262
        }
263
264
        return 'string';
265
    }
266
267
    /**
268
     * @todo  check escaping
269
     *
270
     * @param $cellFormat
271
     *
272
     * @return string
273
     */
274
    private function escapeCellFormat($cellFormat)
275
    {
276
        $ignoreUntil = '';
277
        $escaped = '';
278
        for ($i = 0, $ix = strlen($cellFormat); $i < $ix; $i++) {
279
            $c = $cellFormat[$i];
280
            if ($ignoreUntil == '' && $c == '[') {
281
                $ignoreUntil = ']';
282
            } else {
283
                if ($ignoreUntil == '' && $c == '"') {
284
                    $ignoreUntil = '"';
285
                } else {
286
                    if ($ignoreUntil == $c) {
287
                        $ignoreUntil = '';
288
                    }
289
                }
290
            }
291
            if ($ignoreUntil == '' &&
292
                ($c == ' ' || $c == '-' || $c == '(' || $c == ')') &&
293
                ($i == 0 || $cellFormat[$i - 1] != '_')
294
            ) {
295
                $escaped .= "\\".$c;
296
            } else {
297
                $escaped .= $c;
298
            }
299
        }
300
301
        return $escaped;
302
    }
303
304
    /**
305
     * backwards compatibility
306
     *
307
     * @param string $cellFormat
308
     *
309
     * @return int|mixed
310
     */
311
    private function addCellFormat($cellFormat)
312
    {
313
        $cellFormat = strtoupper($this->getCellFormat($cellFormat));
314
        $position = array_search($cellFormat, $this->cellFormats, $strict = true);
315
        if ($position === false) {
316
            $position = count($this->cellFormats);
317
            $this->cellFormats[] = $this->escapeCellFormat($cellFormat);
318
            $this->cellTypes[] = $this->determineCellType($cellFormat);
319
        }
320
321
        return $position;
322
    }
323
324
    /**
325
     * @param string $cellFormat
326
     *
327
     * @return string
328
     */
329
    private function getCellFormat($cellFormat)
330
    {
331
        switch ($cellFormat) {
332
            case 'string':
333
                $cellFormat = 'GENERAL';
334
                break;
335
            case 'integer':
336
                $cellFormat = '0';
337
                break;
338
            case 'date':
339
                $cellFormat = 'YYYY-MM-DD';
340
                break;
341
            case 'datetime':
342
                $cellFormat = 'YYYY-MM-DD HH:MM:SS';
343
                break;
344
            case 'dollar':
345
                $cellFormat = '[$$-1009]#,##0.00;[RED]-[$$-1009]#,##0.00';
346
                break;
347
            case 'money':
348
                $cellFormat = '[$$-1009]#,##0.00;[RED]-[$$-1009]#,##0.00';
349
                break;
350
            case 'euro':
351
                $cellFormat = '#,##0.00 [$€-407];[RED]-#,##0.00 [$€-407]';
352
                break;
353
            case 'NN':
354
                $cellFormat = 'DDD';
355
                break;
356
            case 'NNN':
357
                $cellFormat = 'DDDD';
358
                break;
359
            case 'NNNN':
360
                $cellFormat = 'DDDD", "';
361
                break;
362
        }
363
364
        return $cellFormat;
365
    }
366
367
    /**
368
     * @param string $sheetName
369
     * @param array $headerTypes
370
     * @param bool $suppressRow
371
     */
372
    public function writeSheetHeader($sheetName, array $headerTypes, $suppressRow = false)
373
    {
374
        if (empty($sheetName) || empty($headerTypes) || !empty($this->sheets[$sheetName])) {
375
            return;
376
        }
377
        $this->initializeSheet($sheetName);
378
        /** @var Sheet $sheet */
379
        $sheet = &$this->sheets[$sheetName];
380
        $sheet->setColumns([]);
381
        foreach ($headerTypes as $val) {
382
            $sheet->setColumn($this->addCellFormat($val));
383
        }
384
        if (!$suppressRow) {
385
            $this->writeRowHeader($sheet, array_keys($headerTypes));
386
            $sheet->increaseRowCount();
387
        }
388
        $this->currentSheet = $sheetName;
389
    }
390
391
    /**
392
     * @param Sheet $sheet
393
     * @param array $headerRow
394
     */
395
    private function writeRowHeader(Sheet $sheet, $headerRow)
396
    {
397
        $sheet->getWriter()->write(
398
            '<row collapsed="false" customFormat="false" 
399
                customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.(1).'">'
400
        );
401
        foreach ($headerRow as $k => $v) {
402
            $this->writeCell($sheet->getWriter(), 0, $k, $v, $cell_format_index = '0');
403
        }
404
        $sheet->getWriter()->write('</row>');
405
    }
406
407
    /**
408
     * @param string $sheetName
409
     * @param array $row
410
     */
411
    public function writeSheetRow($sheetName, array $row)
412
    {
413
        if (empty($sheetName) || empty($row)) {
414
            return;
415
        }
416
        $this->initializeSheet($sheetName);
417
        /** @var Sheet $sheet */
418
        $sheet = &$this->sheets[$sheetName];
419
        $columns = $sheet->getColumns();
420
        if (empty($columns)) {
421
            $sheet->setColumns(array_fill($from = 0, $until = count($row), '0'));
422
        }
423
        $sheet->getWriter()->write(
424
            '<row collapsed="false" customFormat="false" customHeight="false" 
425
            hidden="false" ht="12.1" outlineLevel="0" r="'.($sheet->getRowCount() + 1).'">'
426
        );
427
        $column_count = 0;
428
        $sheetColumns = $sheet->getColumns();
429
        foreach ($row as $k => $v) {
430
            $this->writeCell(
431
                $sheet->getWriter(),
432
                $sheet->getRowCount(),
433
                $column_count,
434
                $v,
435
                $sheetColumns[$column_count]
436
            );
437
            $column_count++;
438
        }
439
        $sheet->getWriter()->write('</row>');
440
        $sheet->increaseRowCount();
441
        $this->currentSheet = $sheetName;
442
    }
443
444
    /**
445
     * @param string $sheetName
446
     */
447
    protected function finalizeSheet($sheetName)
448
    {
449
        if (empty($sheetName) || $this->sheets[$sheetName]->getFinalized()) {
450
            return;
451
        }
452
        /** @var Sheet $sheet */
453
        $sheet = &$this->sheets[$sheetName];
454
        $sheet->getWriter()->write('</sheetData>');
455
        $mergeCells = $sheet->getMergeCells();
456
        if (!empty($mergeCells)) {
457
            $sheet->getWriter()->write('<mergeCells>');
458
            foreach ($mergeCells as $range) {
459
                $sheet->getWriter()->write('<mergeCell ref="'.$range.'"/>');
460
            }
461
            $sheet->getWriter()->write('</mergeCells>');
462
        }
463
        $sheet->getWriter()->write(
464
            '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false"
465
                verticalCentered="false"/>'
466
        );
467
        $sheet->getWriter()->write(
468
            '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>'
469
        );
470
        $sheet->getWriter()->write(
471
            '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" 
472
                fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" 
473
                paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>'
474
        );
475
        $sheet->getWriter()->write('<headerFooter differentFirst="false" differentOddEven="false">');
476
        $sheet->getWriter()->write(
477
            '<oddHeader>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12&amp;A</oddHeader>'
478
        );
479
        $sheet->getWriter()->write(
480
            '<oddFooter>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12Page &amp;P</oddFooter>'
481
        );
482
        $sheet->getWriter()->write('</headerFooter>');
483
        $sheet->getWriter()->write('</worksheet>');
484
        $maxCell = self::xlsCell($sheet->getRowCount() - 1, count($sheet->getColumns()) - 1);
485
        $maxCellTag = '<dimension ref="A1:'.$maxCell.'"/>';
486
        $paddingLength = $sheet->getMaxCellTagEnd() - $sheet->getMaxCellTagStart() - strlen($maxCellTag);
487
        $sheet->getWriter()->fSeek($sheet->getMaxCellTagStart());
488
        $sheet->getWriter()->write($maxCellTag.str_repeat(" ", $paddingLength));
489
        $sheet->getWriter()->close();
490
        $sheet->setFinalized(true);
491
    }
492
493
    /**
494
     * @param string $sheetName
495
     * @param int $startCellRow
496
     * @param int $startCellColumn
497
     * @param int $endCellRow
498
     * @param int $endCellColumn
499
     */
500
    public function markMergedCell($sheetName, $startCellRow, $startCellColumn, $endCellRow, $endCellColumn)
501
    {
502
        if (empty($sheetName) || $this->sheets[$sheetName]->getFinalized()) {
503
            return;
504
        }
505
        $this->initializeSheet($sheetName);
506
        /** @var Sheet $sheet */
507
        $sheet = &$this->sheets[$sheetName];
508
        $startCell = self::xlsCell($startCellRow, $startCellColumn);
509
        $endCell = self::xlsCell($endCellRow, $endCellColumn);
510
        $sheet->setMergeCells($startCell.":".$endCell);
511
    }
512
513
    /**
514
     * @param array $data
515
     * @param string $sheetName
516
     * @param array $headerTypes
517
     */
518
    public function writeSheet(array $data, $sheetName = '', array $headerTypes = [])
519
    {
520
        $sheetName = empty($sheetName) ? 'Sheet1' : $sheetName;
521
        $data = empty($data) ? [['']] : $data;
522
        if (!empty($headerTypes)) {
523
            $this->writeSheetHeader($sheetName, $headerTypes);
524
        }
525
        foreach ($data as $i => $row) {
526
            $this->writeSheetRow($sheetName, $row);
527
        }
528
        $this->finalizeSheet($sheetName);
529
    }
530
531
    /**
532
     * @param Writer $file
533
     * @param $rowNumber
534
     * @param $columnNumber
535
     * @param $value
536
     * @param $cellIndex
537
     */
538
    protected function writeCell(
539
        Writer $file,
540
        $rowNumber,
541
        $columnNumber,
542
        $value,
543
        $cellIndex
544
    ) {
545
        $cellType = $this->cellTypes[$cellIndex];
546
        $cellName = self::xlsCell($rowNumber, $columnNumber);
547
        if (!is_scalar($value) || $value === '') {
548
            $file->write('<c r="'.$cellName.'" s="'.$cellIndex.'"/>');
549
        } elseif (is_string($value) && $value{0} == '=') {
550
            $file->write(
551
                sprintf('<c r="%s" s="%s" t="s"><f>%s</f></c>', $cellName, $cellIndex, self::xmlspecialchars($value))
552
            );
553
        } elseif ($cellType == 'date') {
554
            $file->write(
555
                sprintf('<c r="%s" s="%s" t="n"><v>%s</v></c>', $cellName, $cellIndex, self::convertDateTime($value))
556
            );
557
        } elseif ($cellType == 'datetime') {
558
            $file->write(
559
                '<c r="'.$cellName.'" s="'.$cellIndex.'" t="n"><v>'.self::convertDateTime($value).'</v></c>'
560
            );
561
        } elseif ($cellType == 'currency' || $cellType == 'percent' || $cellType == 'numeric') {
562
            $file->write(
563
                '<c r="'.$cellName.'" s="'.$cellIndex.'" t="n"><v>'.self::xmlspecialchars($value).'</v></c>'
564
            );
565
        } else {
566
            if (!is_string($value)) {
567
                $file->write('<c r="'.$cellName.'" s="'.$cellIndex.'" t="n"><v>'.($value * 1).'</v></c>');
568
            } else {
569
                if ($value{0} != '0' && $value{0} != '+' && filter_var(
570
                    $value,
571
                    FILTER_VALIDATE_INT,
572
                    ['options' => ['max_range' => self::EXCEL_MAX_RANGE]]
573
                )) {
574
                    $file->write('<c r="'.$cellName.'" s="'.$cellIndex.'" t="n"><v>'.($value * 1).'</v></c>');
575
                } else {
576
                    $file->write(
577
                        '<c r="'.$cellName.'" s="'.$cellIndex.'" t="s"><v>'.self::xmlspecialchars(
578
                            $this->setSharedString($value)
579
                        ).'</v></c>'
580
                    );
581
                }
582
            }
583
        }
584
    }
585
586
    /**
587
     * @return string
588
     */
589
    protected function writeStylesXML()
590
    {
591
        $temporaryFilename = $this->tempFilename();
592
        $file = new Writer($temporaryFilename);
593
        $styles = new Styles();
594
        $styles->setCellFormats($this->cellFormats);
595
        $file->write($styles->buildStylesXML());
596
597
        return $temporaryFilename;
598
    }
599
600
    /**
601
     * @param $v
602
     *
603
     * @return int|mixed
604
     */
605
    protected function setSharedString($v)
606
    {
607
        if (isset($this->sharedStrings[$v])) {
608
            $stringValue = $this->sharedStrings[$v];
609
        } else {
610
            $stringValue = count($this->sharedStrings);
611
            $this->sharedStrings[$v] = $stringValue;
612
        }
613
        $this->sharedStringCount++;
614
615
        return $stringValue;
616
    }
617
618
    /**
619
     * @return string
620
     */
621
    protected function writeSharedStringsXML()
622
    {
623
        $tempFilename = $this->tempFilename();
624
        $file = new Writer($tempFilename);
625
        $sharedStrings = new SharedStrings($this->sharedStringCount, $this->sharedStrings);
626
        $file->write($sharedStrings->buildSharedStringsXML());
627
        $file->close();
628
629
        return $tempFilename;
630
    }
631
632
    /**
633
     * @param int $rowNumber
634
     * @param int $columnNumber
635
     *
636
     * @return string Cell label/coordinates (A1, C3, AA42)
637
     */
638
    public static function xlsCell($rowNumber, $columnNumber)
639
    {
640
        $n = $columnNumber;
641
        for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
642
            $r = chr($n % 26 + 0x41).$r;
643
        }
644
645
        return $r.($rowNumber + 1);
646
    }
647
648
    /**
649
     * @param $string
650
     */
651
    public static function log($string)
652
    {
653
        file_put_contents(
654
            "php://stderr",
655
            date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n"
656
        );
657
    }
658
659
    /**
660
     * @link https://msdn.microsoft.com/ru-RU/library/aa365247%28VS.85%29.aspx
661
     *
662
     * @param string $filename
663
     *
664
     * @return mixed
665
     */
666
    public static function checkFilename($filename)
667
    {
668
        $invalidCharacter = array_merge(
669
            array_map('chr', range(0, 31)),
670
            ['<', '>', '?', '"', ':', '|', '\\', '/', '*', '&']
671
        );
672
673
        return str_replace($invalidCharacter, '', $filename);
674
    }
675
676
    /**
677
     * @param $val
678
     *
679
     * @return mixed
680
     */
681
    public static function xmlspecialchars($val)
682
    {
683
        return str_replace("'", "&#39;", htmlspecialchars($val));
684
    }
685
686
    /**
687
     * @param string $dateInput
688
     *
689
     * @return mixed
690
     */
691
    public static function convertDateTime($dateInput)
692
    {
693
        $epoch = 1900;
694
        $norm = 300;
695
        $year = $month = $day = $offset = $seconds = 0;
696
        $dateTime = $dateInput;
697
        if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $dateTime, $matches)) {
698
            list($full, $year, $month, $day) = $matches;
0 ignored issues
show
Unused Code introduced by
The assignment to $full is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
699
        }
700
        if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $dateTime, $matches)) {
701
            list($full, $hour, $min, $sec) = $matches;
0 ignored issues
show
Unused Code introduced by
The assignment to $full is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
702
            $seconds = ($hour * 60 * 60 + $min * 60 + $sec) / 86400;
703
        }
704
        if ("$year-$month-$day" == '1899-12-31' || "$year-$month-$day" == '1900-01-00') {
705
            return $seconds;
706
        }
707
        if ("$year-$month-$day" == '1900-02-29') {
708
            return 60 + $seconds;
709
        }
710
711
        $range  = $year - $epoch;
712
        // check leapDay
713
        $leap = (new \DateTime($dateInput))->format('L');
714
        $mDays = [31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
715
716
        if (($year < $epoch || $year > 9999) || ($month < 1 || $month > 12) || $day < 1 || $day > $mDays[$month - 1]) {
717
            return 0;
718
        }
719
720
        $days = $day + ($range * 365) + array_sum(array_slice($mDays, 0, $month - 1));
721
        $days += intval(($range) / 4) -  intval(($range + $offset) / 100);
722
        $days += intval(($range + $offset + $norm) / 400) - intval($leap);
723
724
        if ($days > 59) {
725
            $days++;
726
        }
727
        return  $days + $seconds;
728
    }
729
}
730