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 ( d28d59...eb929a )
by Denis
03:56
created

ExcelWriter::determineCellType()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 42
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 27
nc 7
nop 1
1
<?php
2
namespace Ellumilel;
3
4
use Ellumilel\DocProps\App;
5
use Ellumilel\DocProps\Core;
6
use Ellumilel\Helpers\ExcelHelper;
7
use Ellumilel\Rels\Relationships;
8
use Ellumilel\Xl\SharedStrings;
9
use Ellumilel\Xl\Styles;
10
use Ellumilel\Xl\Workbook;
11
use Ellumilel\Xl\Worksheets\SheetXml;
12
13
/**
14
 * Class ExcelWriter
15
 * @package Ellumilel
16
 * @author Denis Tikhonov <[email protected]>
17
 */
18
class ExcelWriter
19
{
20
    /** @var array */
21
    protected $sheets = [];
22
    /** @var array */
23
    protected $sharedStrings = [];
24
    /** @var int */
25
    protected $sharedStringCount = 0;
26
    /** @var array */
27
    protected $tempFiles = [];
28
    /** @var array */
29
    protected $cellFormats = [];
30
    /** @var array */
31
    protected $cellTypes = [];
32
    /** @var string  */
33
    protected $currentSheet = '';
34
    /** @var null */
35
    protected $tmpDir = null;
36
    /** @var null */
37
    protected $fileName = null;
38
    /** @var Core */
39
    protected $core;
40
    /** @var App */
41
    protected $app;
42
    /** @var Workbook */
43
    protected $workbook;
44
    /** @var SheetXml */
45
    protected $sheetXml;
46
47
    /**
48
     * ExcelWriter constructor.
49
     * @throws \Exception
50
     */
51
    public function __construct()
52
    {
53
        if (!class_exists('ZipArchive')) {
54
            throw new \Exception('ZipArchive not found');
55
        }
56
        if (!ini_get('date.timezone')) {
57
            date_default_timezone_set('UTC');
58
        }
59
        $this->addCellFormat($cell_format = 'GENERAL');
60
        $this->core = new Core();
61
        $this->app = new App();
62
        $this->workbook = new Workbook();
63
        $this->sheetXml = new SheetXml();
64
    }
65
66
    /**
67
     * @param string $author
68
     */
69
    public function setAuthor($author)
70
    {
71
        $this->core->setAuthor($author);
72
    }
73
74
    public function __destruct()
75
    {
76
        if (!empty($this->tempFiles)) {
77
            foreach ($this->tempFiles as $tempFile) {
78
                if (file_exists($tempFile)) {
79
                    unlink($tempFile);
80
                }
81
            }
82
        }
83
    }
84
85
    /**
86
     * @param $dir
87
     */
88
    public function setTmpDir($dir)
89
    {
90
        $this->tmpDir = $dir;
91
    }
92
93
    /**
94
     * Set output filename: yourFileName.xlsx
95
     *
96
     * @param string $fileName
97
     */
98
    public function setFileName($fileName)
99
    {
100
        $this->fileName = $fileName;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fileName of type string is incompatible with the declared type null of property $fileName.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
101
    }
102
103
    /**
104
     * Return tmpFileName
105
     * @return string
106
     */
107
    protected function tempFilename()
108
    {
109
        $tmpDir = is_null($this->tmpDir) ? sys_get_temp_dir() : $this->tmpDir;
110
        $filename = tempnam($tmpDir, "excelWriter_");
111
        $this->tempFiles[] = $filename;
112
113
        return $filename;
114
    }
115
116
    /**
117
     * @param bool $headers
118
     */
119
    public function writeToStdOut($headers = true)
120
    {
121
        if (empty($this->tmpDir)) {
122
            $tempFile = $this->tempFilename().'.xlsx';
123
        } else {
124
            $tempFile = $this->fileName;
125
        }
126
127
        $this->writeToFile($tempFile);
128
        if (file_exists($tempFile)) {
129
            if ($headers) {
130
                header('Content-Description: File Transfer');
131
                header('Content-Type: application/octet-stream');
132
                header('Content-Disposition: attachment; filename="'.basename($tempFile).'"');
133
                header('Expires: 0');
134
                header('Cache-Control: must-revalidate');
135
                header('Pragma: public');
136
                header('Content-Length: '.filesize($tempFile));
137
            }
138
            readfile($tempFile);
139
        }
140
    }
141
142
    /**
143
     * @return string
144
     */
145
    public function writeToString()
146
    {
147
        $tempFile = $this->tempFilename();
148
        $this->writeToFile($tempFile);
149
        $string = file_get_contents($tempFile);
150
151
        return $string;
152
    }
153
154
    /**
155
     * @param string $filename
156
     */
157
    public function writeToFile($filename)
158
    {
159
        $zip = new \ZipArchive();
160
        foreach ($this->sheets as $sheetName => $sheet) {
161
            $this->finalizeSheet($sheetName);
162
        }
163
        $this->checkAndUnlink($zip, $filename);
164
        $this->workbook->setSheet($this->sheets);
165
166
        $contentTypes = new ContentTypes(!empty($this->sharedStrings));
167
        $contentTypes->setSheet($this->sheets);
168
169
        $rel = new Relationships(!empty($this->sharedStrings));
170
        $rel->setSheet($this->sheets);
171
        $zip->addEmptyDir("docProps/");
172
        $zip->addFromString("docProps/app.xml", $this->app->buildAppXML());
173
        $zip->addFromString("docProps/core.xml", $this->core->buildCoreXML());
174
        $zip->addEmptyDir("_rels/");
175
        $zip->addFromString("_rels/.rels", $rel->buildRelationshipsXML());
176
        $zip->addEmptyDir("xl/worksheets/");
177
        foreach ($this->sheets as $sheet) {
178
            /** @var Sheet $sheet */
179
            $zip->addFile($sheet->getFilename(), "xl/worksheets/".$sheet->getXmlName());
180
        }
181
        if (!empty($this->sharedStrings)) {
182
            $zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml");
183
        }
184
        $zip->addFromString("xl/workbook.xml", $this->workbook->buildWorkbookXML());
185
        $zip->addFile($this->writeStylesXML(), "xl/styles.xml");
186
        $zip->addFromString("[Content_Types].xml", $contentTypes->buildContentTypesXML());
187
        $zip->addEmptyDir("xl/_rels/");
188
        $zip->addFromString("xl/_rels/workbook.xml.rels", $rel->buildWorkbookRelationshipsXML());
189
        $zip->close();
190
    }
191
192
    /**
193
     * @param string $sheetName
194
     */
195
    protected function initializeSheet($sheetName)
196
    {
197
        if ($this->currentSheet == $sheetName || isset($this->sheets[$sheetName])) {
198
            return;
199
        }
200
        $sheetFilename = $this->tempFilename();
201
        $sheetXmlName = 'sheet' . (count($this->sheets) + 1).".xml";
202
        $sheetObj = new Sheet();
203
        $sheetObj
204
            ->setFilename($sheetFilename)
205
            ->setSheetName($sheetName)
206
            ->setXmlName($sheetXmlName)
207
            ->setWriter(new Writer($sheetFilename))
208
        ;
209
        $this->sheets[$sheetName] = $sheetObj;
210
        /** @var Sheet $sheet */
211
        $sheet = &$this->sheets[$sheetName];
212
        $selectedTab = count($this->sheets) == 1 ? 'true' : 'false';
213
        $maxCell = ExcelHelper::xlsCell(ExcelHelper::EXCEL_MAX_ROW, ExcelHelper::EXCEL_MAX_COL);
214
        $sheet->getWriter()->write($this->sheetXml->getXml());
215
        $sheet->getWriter()->write($this->sheetXml->getWorksheet());
216
        $sheet->getWriter()->write($this->sheetXml->getSheetPr());
217
        $sheet->setMaxCellTagStart($sheet->getWriter()->fTell());
218
        $sheet->getWriter()->write($this->sheetXml->getDimension($maxCell));
219
        $sheet->setMaxCellTagEnd($sheet->getWriter()->fTell());
220
        $sheet->getWriter()->write($this->sheetXml->getSheetViews($selectedTab));
221
        $sheet->getWriter()->write($this->sheetXml->getCools());
222
        $sheet->getWriter()->write('<sheetData>');
223
    }
224
225
    /**
226
     * @param $cellFormat
227
     *
228
     * @return string
229
     */
230
    private function determineCellType($cellFormat)
231
    {
232
        $cellFormat = str_replace("[RED]", "", $cellFormat);
233
        if ($cellFormat == 'GENERAL') {
234
            return 'string';
235
        }
236
        if ($cellFormat == '0') {
237
            return 'numeric';
238
        }
239
        if ($cellFormat == '@') {
240
            return '@';
241
        }
242
        $checkArray = [
243
            'datetime' => [
244
                "/[H]{1,2}:[M]{1,2}/",
245
                "/[M]{1,2}:[S]{1,2}/",
246
            ],
247
            'numeric' => [
248
                "/0/",
249
            ],
250
            'date' => [
251
                "/[YY]{2,4}/",
252
                "/[D]{1,2}/",
253
                "/[M]{1,2}/",
254
            ],
255
            'currency' => [
256
                "/$/",
257
            ],
258
            'percent' => [
259
                "/%/",
260
            ],
261
        ];
262
        foreach ($checkArray as $type => $item) {
263
            foreach ($item as $prMatch) {
264
                if (preg_match($prMatch, $cellFormat)) {
265
                    return $type;
266
                }
267
            }
268
        }
269
270
        return 'string';
271
    }
272
273
    /**
274
     * backwards compatibility
275
     *
276
     * @param $cellFormat
277
     *
278
     * @return int|mixed
279
     */
280
    private function addCellFormat($cellFormat)
281
    {
282
        $cellFormat = strtoupper($this->getCellFormat($cellFormat));
283
        $position = array_search($cellFormat, $this->cellFormats, $strict = true);
284
        if ($position === false) {
285
            $position = count($this->cellFormats);
286
            $this->cellFormats[] = ExcelHelper::escapeCellFormat($cellFormat);
287
            $this->cellTypes[] = $this->determineCellType($cellFormat);
288
        }
289
290
        return $position;
291
    }
292
293
    /**
294
     * @link https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformats(v=office.15).aspx
295
     *
296
     * @param string $cellFormat
297
     *
298
     * @return string
299
     */
300
    private function getCellFormat($cellFormat)
301
    {
302
        $formatArray = [
303
            'string' => 'GENERAL',
304
            'text' => '@',
305
            'integer' => '0',
306
            'float_with_sep' => '#,##0.00',
307
            'float' => '0.00',
308
            'date' => 'YYYY-MM-DD',
309
            'datetime' => 'YYYY-MM-DD HH:MM:SS',
310
            'dollar' => '[$$-1009]#,##0.00;[RED]-[$$-1009]#,##0.00',
311
            'money' => '[$$-1009]#,##0.00;[RED]-[$$-1009]#,##0.00',
312
            'euro' => '#,##0.00 [$€-407];[RED]-#,##0.00 [$€-407]',
313
            'rub' => '#,##0.00 [$₽-419];[Red]-#,##0.00 [$₽-419]',
314
            'NN' => 'DDD',
315
            'NNN' => 'DDDD',
316
            'NNNN' => 'DDDD", "',
317
        ];
318
319
        if (array_key_exists($cellFormat, $formatArray)) {
320
            return $formatArray[$cellFormat];
321
        }
322
        return $cellFormat;
323
    }
324
325
    /**
326
     * @param string $sheetName
327
     * @param array $headerTypes
328
     * @param bool $suppressRow
329
     */
330
    public function writeSheetHeader($sheetName, array $headerTypes, $suppressRow = false)
331
    {
332
        if (empty($sheetName) || empty($headerTypes) || !empty($this->sheets[$sheetName])) {
333
            return;
334
        }
335
        $this->initializeSheet($sheetName);
336
        /** @var Sheet $sheet */
337
        $sheet = &$this->sheets[$sheetName];
338
        $sheet->setColumns([]);
339
        foreach ($headerTypes as $val) {
340
            $sheet->setColumn($this->addCellFormat($val));
341
        }
342
        if (!$suppressRow) {
343
            $this->writeRowHeader($sheet, array_keys($headerTypes));
344
            $sheet->increaseRowCount();
345
        }
346
        $this->currentSheet = $sheetName;
347
    }
348
349
    /**
350
     * @param Sheet $sheet
351
     * @param array $headerRow
352
     */
353
    private function writeRowHeader(Sheet $sheet, $headerRow)
354
    {
355
        $sheet->getWriter()->write(
356
            '<row collapsed="false" customFormat="false" 
357
                customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.(1).'">'
358
        );
359
        foreach ($headerRow as $k => $v) {
360
            $this->writeCell($sheet->getWriter(), 0, $k, $v, $cell_format_index = '0');
361
        }
362
        $sheet->getWriter()->write('</row>');
363
    }
364
365
    /**
366
     * @param string $sheetName
367
     * @param array $row
368
     */
369
    public function writeSheetRow($sheetName, array $row)
370
    {
371
        if (empty($sheetName) || empty($row)) {
372
            return;
373
        }
374
        $this->initializeSheet($sheetName);
375
        /** @var Sheet $sheet */
376
        $sheet = &$this->sheets[$sheetName];
377
        $columns = $sheet->getColumns();
378
        if (empty($columns)) {
379
            $sheet->setColumns(array_fill(0, count($row), '0'));
380
        }
381
        $sheet->getWriter()->write(
382
            '<row collapsed="false" customFormat="false" customHeight="false" 
383
            hidden="false" ht="12.1" outlineLevel="0" r="'.($sheet->getRowCount() + 1).'">'
384
        );
385
        $column_count = 0;
386
        $sheetColumns = $sheet->getColumns();
387
        foreach ($row as $k => $v) {
388
            $this->writeCell(
389
                $sheet->getWriter(),
390
                $sheet->getRowCount(),
391
                $column_count,
392
                $v,
393
                $sheetColumns[$column_count]
394
            );
395
            $column_count++;
396
        }
397
        $sheet->getWriter()->write('</row>');
398
        $sheet->increaseRowCount();
399
        $this->currentSheet = $sheetName;
400
    }
401
402
    /**
403
     * @param string $sheetName
404
     */
405
    protected function finalizeSheet($sheetName)
406
    {
407
        if (empty($sheetName) || ($this->sheets[$sheetName] instanceof Sheet &&
408
                $this->sheets[$sheetName]->getFinalized()
409
            )
410
        ) {
411
            return;
412
        }
413
414
        /** @var Sheet $sheet */
415
        $sheet = &$this->sheets[$sheetName];
416
        $sheet->getWriter()->write('</sheetData>');
417
        $mergeCells = $sheet->getMergeCells();
418
        if (!empty($mergeCells)) {
419
            $sheet->getWriter()->write($this->sheetXml->getMergeCells($mergeCells));
420
        }
421
        $sheet->getWriter()->write($this->sheetXml->getPrintOptions());
422
        $sheet->getWriter()->write($this->sheetXml->getPageMargins());
423
        $sheet->getWriter()->write($this->sheetXml->getPageSetup());
424
        $sheet->getWriter()->write($this->sheetXml->getHeaderFooter());
425
        $sheet->getWriter()->write('</worksheet>');
426
        $maxCell = ExcelHelper::xlsCell($sheet->getRowCount() - 1, count($sheet->getColumns()) - 1);
427
        $maxCellTag = $this->sheetXml->getDimension($maxCell);
428
        $paddingLength = $sheet->getMaxCellTagEnd() - $sheet->getMaxCellTagStart() - strlen($maxCellTag);
429
        $sheet->getWriter()->fSeek($sheet->getMaxCellTagStart());
430
        $sheet->getWriter()->write($maxCellTag.str_repeat(" ", $paddingLength));
431
        $sheet->getWriter()->close();
432
        $sheet->setFinalized(true);
433
    }
434
435
    /**
436
     * @param string $sheetName
437
     * @param int $startCellRow
438
     * @param int $startCellColumn
439
     * @param int $endCellRow
440
     * @param int $endCellColumn
441
     */
442
    public function markMergedCell($sheetName, $startCellRow, $startCellColumn, $endCellRow, $endCellColumn)
443
    {
444
        if (empty($sheetName) || $this->sheets[$sheetName]->getFinalized()) {
445
            return;
446
        }
447
        $this->initializeSheet($sheetName);
448
        /** @var Sheet $sheet */
449
        $sheet = &$this->sheets[$sheetName];
450
        $startCell = ExcelHelper::xlsCell($startCellRow, $startCellColumn);
451
        $endCell = ExcelHelper::xlsCell($endCellRow, $endCellColumn);
452
        $sheet->setMergeCells($startCell.":".$endCell);
453
    }
454
455
    /**
456
     * @param array $data
457
     * @param string $sheetName
458
     * @param array $headerTypes
459
     */
460
    public function writeSheet(array $data, $sheetName = '', array $headerTypes = [])
461
    {
462
        $sheetName = empty($sheetName) ? 'Sheet1' : $sheetName;
463
        $data = empty($data) ? [['']] : $data;
464
        if (!empty($headerTypes)) {
465
            $this->writeSheetHeader($sheetName, $headerTypes);
466
        }
467
        foreach ($data as $i => $row) {
468
            $this->writeSheetRow($sheetName, $row);
469
        }
470
        $this->finalizeSheet($sheetName);
471
    }
472
473
    /**
474
     * @param Writer $file
475
     * @param int $rowNumber
476
     * @param int $columnNumber
477
     * @param mixed $value
478
     * @param $cellIndex
479
     */
480
    protected function writeCell(Writer $file, $rowNumber, $columnNumber, $value, $cellIndex)
481
    {
482
        $cellType = $this->cellTypes[$cellIndex];
483
        $cellName = ExcelHelper::xlsCell($rowNumber, $columnNumber);
484
        $cell = $this->sheetXml->getCell($cellName, $cellIndex, $cellType, $value);
485
        if ($cell === false) {
486
            $file->write(
487
                '<c r="'.$cellName.'" s="'.$cellIndex.'" t="s"><v>'.ExcelHelper::xmlspecialchars(
488
                    $this->setSharedString($value)
489
                ).'</v></c>'
490
            );
491
        } else {
492
            $file->write($cell);
493
        }
494
    }
495
496
    /**
497
     * @return string
498
     */
499
    protected function writeStylesXML()
500
    {
501
        $temporaryFilename = $this->tempFilename();
502
        $file = new Writer($temporaryFilename);
503
        $styles = new Styles();
504
        $styles->setCellFormats($this->cellFormats);
505
        $file->write($styles->buildStylesXML());
506
507
        return $temporaryFilename;
508
    }
509
510
    /**
511
     * @param $v
512
     *
513
     * @return int|mixed
514
     */
515
    protected function setSharedString($v)
516
    {
517
        if (isset($this->sharedStrings[$v])) {
518
            $stringValue = $this->sharedStrings[$v];
519
        } else {
520
            $stringValue = count($this->sharedStrings);
521
            $this->sharedStrings[$v] = $stringValue;
522
        }
523
        $this->sharedStringCount++;
524
525
        return $stringValue;
526
    }
527
528
    /**
529
     * @return string
530
     */
531
    protected function writeSharedStringsXML()
532
    {
533
        $tempFilename = $this->tempFilename();
534
        $file = new Writer($tempFilename);
535
        $sharedStrings = new SharedStrings($this->sharedStringCount, $this->sharedStrings);
536
        $file->write($sharedStrings->buildSharedStringsXML());
537
        $file->close();
538
539
        return $tempFilename;
540
    }
541
542
    /**
543
     * @param \ZipArchive $zip
544
     * @param string $filename
545
     */
546
    private function checkAndUnlink(\ZipArchive $zip, $filename)
547
    {
548
        if (file_exists($filename) && is_writable($filename)) {
549
            unlink($filename);
550
        }
551
        if (empty($this->sheets) || !$zip->open($filename, \ZipArchive::CREATE)) {
552
            throw new \RuntimeException(
553
                "Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined or unable to create zip."
554
            );
555
        }
556
    }
557
}
558