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 ( 78d09e...a6964a )
by Denis
02:46
created

ExcelWriter::writeToFile()   B

Complexity

Conditions 8
Paths 26

Size

Total Lines 56
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 56
rs 7.3333
cc 8
eloc 39
nc 26
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
587
        //$file->write(		'<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
588
        //$file->write(		'<numFmt formatCode="YYYY-MM-DD\ HH:MM:SS" numFmtId="166"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
589
        //$file->write(		'<numFmt formatCode="YYYY-MM-DD" numFmtId="167"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
590
        $file->write('</numFmts>');
591
        $file->write('<fonts count="4">');
592
        $file->write('<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
593
        $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
594
        $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
595
        $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
596
        $file->write('</fonts>');
597
        $file->write('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
598
        $file->write('<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>');
599
        $file->write('<cellStyleXfs count="20">');
600
        $file->write('<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
601
        $file->write('<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
602
        $file->write('<protection hidden="false" locked="true"/>');
603
        $file->write('</xf>');
604
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
605
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
606
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
607
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
608
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
609
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
610
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
611
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
612
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
613
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
614
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
615
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
616
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
617
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
618
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>');
619
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>');
620
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
621
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
622
        $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
623
        $file->write('</cellStyleXfs>');
624
        $file->write('<cellXfs count="'.count($this->cellFormats).'">');
625
        foreach ($this->cellFormats as $i => $v) {
626
            $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="false" 
627
            applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="'.(164+$i).'" xfId="0"/>');
628
        }
629
        $file->write('</cellXfs>');
630
        //$file->write(	'<cellXfs count="4">');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
631
        //$file->write('<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
632
        //$file->write('<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
633
        //$file->write('<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
634
        //$file->write('<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
635
        //$file->write(	'</cellXfs>');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
636
        $file->write('<cellStyles count="6">');
637
        $file->write('<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
638
        $file->write('<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
639
        $file->write('<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>');
640
        $file->write('<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>');
641
        $file->write('<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>');
642
        $file->write('<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
643
        $file->write('</cellStyles>');
644
        $file->write('</styleSheet>');
645
        $file->close();
646
        return $temporaryFilename;
647
    }
648
649
    /**
650
     * @param $v
651
     *
652
     * @return int|mixed
653
     */
654
    protected function setSharedString($v)
655
    {
656
        if (isset($this->sharedStrings[$v])) {
657
            $stringValue = $this->sharedStrings[$v];
658
        } else {
659
            $stringValue = count($this->sharedStrings);
660
            $this->sharedStrings[$v] = $stringValue;
661
        }
662
        $this->shared_string_count++;//non-unique count
663
664
        return $stringValue;
665
    }
666
667
    /**
668
     * @return string
669
     */
670
    protected function writeSharedStringsXML()
671
    {
672
        $temporaryFilename = $this->tempFilename();
673
        $file = new Writer($temporaryFilename);
674
        $file->write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
675
        $file->write(
676
            '<sst count="'.($this->shared_string_count).'" uniqueCount="'.count(
677
                $this->sharedStrings
678
            ).'" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
679
        );
680
        foreach ($this->sharedStrings as $s => $c) {
681
            $file->write('<si><t>'.self::xmlspecialchars($s).'</t></si>');
682
        }
683
        $file->write('</sst>');
684
        $file->close();
685
686
        return $temporaryFilename;
687
    }
688
689
    /**
690
     * @return string
691
     */
692
    protected function buildWorkbookXML()
693
    {
694
        $i = 0;
695
        $workbookXml = '';
696
        $workbookXml .= '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
697
        $workbookXml .= '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"';
698
        $workbookXml .= ' xmlns:r="'.$this->urlSchemaFormat.'/relationships">';
699
        $workbookXml .= '<fileVersion appName="Calc"/><workbookPr backupFile="false"';
700
        $workbookXml .= ' showObjects="all" date1904="false"/><workbookProtection/>';
701
        $workbookXml .= '<bookViews><workbookView activeTab="0" firstSheet="0" showHorizontalScroll="true"';
702
        $workbookXml .= ' showSheetTabs="true" showVerticalScroll="true" tabRatio="212" windowHeight="8192"';
703
        $workbookXml .= ' windowWidth="16384" xWindow="0" yWindow="0"/></bookViews>';
704
        $workbookXml .= '<sheets>';
705
        foreach ($this->sheets as $sheet_name => $sheet) {
706
            /** @var Sheet $sheet */
707
            $workbookXml .= '<sheet name="'.self::xmlspecialchars($sheet->getSheetName()).'"';
708
            $workbookXml .= ' sheetId="'.($i + 1).'" state="visible" r:id="rId'.($i + 2).'"/>';
709
            $i++;
710
        }
711
        $workbookXml .= '</sheets>';
712
        $workbookXml .= '<calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/></workbook>';
713
714
        return $workbookXml;
715
    }
716
717
    /**
718
     * @return string
719
     */
720
    protected function buildWorkbookRelXML()
721
    {
722
        $i = 0;
723
        $relXml = '';
724
        $relXml .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
725
        $relXml .= '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
726
        $relXml .= '<Relationship Id="rId1" Type="'.$this->urlSchemaFormat.'/relationships/styles"';
727
        $relXml .= ' Target="styles.xml"/>';
728
        foreach ($this->sheets as $sheet_name => $sheet) {
729
            /** @var Sheet $sheet */
730
            $relXml .= '<Relationship Id="rId'.($i + 2).'" 
731
            Type="'.$this->urlSchemaFormat.'/relationships/worksheet" Target="worksheets/'.($sheet->getXmlName()).'"/>';
732
            $i++;
733
        }
734
        if (!empty($this->sharedStrings)) {
735
            $relXml .= '<Relationship Id="rId'.(count($this->sheets) + 2).'" 
736
            Type="'.$this->urlSchemaFormat.'/relationships/sharedStrings" Target="sharedStrings.xml"/>';
737
        }
738
        $relXml .= "\n";
739
        $relXml .= '</Relationships>';
740
741
        return $relXml;
742
    }
743
744
    /**
745
     * @return string
746
     */
747
    protected function buildContentTypesXML()
748
    {
749
        $xml = '';
750
        $xml .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
751
        $xml .= '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
752
        $xml .= '<Override PartName="/_rels/.rels"';
753
        $xml .= ' ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
754
        $xml .= '<Override PartName="/xl/_rels/workbook.xml.rels"';
755
        $xml .= ' ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
756
        foreach ($this->sheets as $sheet_name => $sheet) {
757
            /** @var Sheet $sheet */
758
            $xml .= '<Override PartName="/xl/worksheets/'.($sheet->getXmlName()).'"';
759
            $xml .= ' ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
760
        }
761
        if (!empty($this->sharedStrings)) {
762
            $xml .= '<Override PartName="/xl/sharedStrings.xml"';
763
            $xml .= ' ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>';
764
        }
765
        $xml .= '<Override PartName="/xl/workbook.xml"';
766
        $xml .= ' ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>';
767
        $xml .= '<Override PartName="/xl/styles.xml"';
768
        $xml .= ' ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
769
        $xml .= '<Override PartName="/docProps/app.xml"';
770
        $xml .= ' ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>';
771
        $xml .= '<Override PartName="/docProps/core.xml"';
772
        $xml .= ' ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>'."\n";
773
        $xml .= '</Types>';
774
775
        return $xml;
776
    }
777
778
    /**
779
     * @param int $rowNumber
780
     * @param int $columnNumber
781
     *
782
     * @return string Cell label/coordinates (A1, C3, AA42)
783
     */
784
    public static function xlsCell($rowNumber, $columnNumber)
785
    {
786
        $n = $columnNumber;
787
        for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
788
            $r = chr($n % 26 + 0x41).$r;
789
        }
790
791
        return $r.($rowNumber + 1);
792
    }
793
794
    /**
795
     * @param $string
796
     */
797
    public static function log($string)
798
    {
799
        file_put_contents(
800
            "php://stderr",
801
            date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n"
802
        );
803
    }
804
805
    /**
806
     * @link https://msdn.microsoft.com/ru-RU/library/aa365247%28VS.85%29.aspx
807
     *
808
     * @param string $filename
809
     *
810
     * @return mixed
811
     */
812
    public static function checkFilename($filename)
813
    {
814
        $invalidCharacter = array_merge(
815
            array_map('chr', range(0, 31)),
816
            ['<', '>', '?', '"', ':', '|', '\\', '/', '*', '&']
817
        );
818
819
        return str_replace($invalidCharacter, '', $filename);
820
    }
821
822
    /**
823
     * @param $val
824
     *
825
     * @return mixed
826
     */
827
    public static function xmlspecialchars($val)
828
    {
829
        return str_replace("'", "&#39;", htmlspecialchars($val));
830
    }
831
832
    /**
833
     * thanks to Excel::Writer::XLSX::Worksheet.pm (perl)
834
     *
835
     * @param string $dateInput
836
     *
837
     * @return int
838
     */
839
    public static function convertDateTime($dateInput)
840
    {
841
        # Time expressed as fraction of 24h hours in seconds
842
        $seconds = 0;
843
        $year = $month = $day = 0;
844
        $hour = $min = $sec = 0;
845
        $dateTime = $dateInput;
846
        if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $dateTime, $matches)) {
847
            list($junk, $year, $month, $day) = $matches;
0 ignored issues
show
Unused Code introduced by
The assignment to $junk 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...
848
        }
849
        if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $dateTime, $matches)) {
850
            list($junk, $hour, $min, $sec) = $matches;
0 ignored issues
show
Unused Code introduced by
The assignment to $junk 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...
851
            $seconds = ($hour * 60 * 60 + $min * 60 + $sec) / (24 * 60 * 60);
852
        }
853
        //using 1900 as epoch, not 1904, ignoring 1904 special case
854
        # Special cases for Excel.
855
        if ("$year-$month-$day" == '1899-12-31') {
856
            return $seconds;
857
        }    # Excel 1900 epoch
858
        if ("$year-$month-$day" == '1900-01-00') {
859
            return $seconds;
860
        }    # Excel 1900 epoch
861
        if ("$year-$month-$day" == '1900-02-29') {
862
            return 60 + $seconds;
863
        }    # Excel false leapday
864
        # We calculate the date by calculating the number of days since the epoch
865
        # and adjust for the number of leap days. We calculate the number of leap
866
        # days by normalising the year in relation to the epoch. Thus the year 2000
867
        # becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
868
        $epoch  = 1900;
869
        $offset = 0;
870
        $norm   = 300;
871
        $range  = $year - $epoch;
872
        # Set month days and check for leap year.
873
        $leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100)) ) ? 1 : 0;
874
        $mdays = array( 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
875
        # Some boundary checks
876
        if ($year < $epoch || $year > 9999) {
877
            return 0;
878
        }
879
        if ($month < 1 || $month > 12) {
880
            return 0;
881
        }
882
        if ($day < 1 || $day > $mdays[$month - 1]) {
883
            return 0;
884
        }
885
        // Accumulate the number of days since the epoch.
886
        $days = $day; # Add days for current month
887
        $days += array_sum(array_slice($mdays, 0, $month - 1)); # Add days for past months
888
        $days += $range * 365; # Add days for past years
889
        $days += intval(($range) / 4); # Add leapdays
890
        $days -= intval(($range + $offset) / 100); # Subtract 100 year leapdays
891
        $days += intval(($range + $offset + $norm) / 400); # Add 400 year leapdays
892
        $days -= $leap; # Already counted above
893
        # Adjust for Excel erroneously treating 1900 as a leap year.
894
        if ($days > 59) {
895
            $days++;
896
        }
897
898
        return $days + $seconds;
899
    }
900
}
901