Completed
Push — develop ( ba7054...942ad7 )
by Adrien
21:30
created

HTML::setMargins()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 1
dl 0
loc 23
ccs 0
cts 20
cp 0
crap 2
rs 9.0856
c 0
b 0
f 0
1
<?php
2
3
namespace PhpSpreadsheet\Writer;
4
5
use Spreadsheet\Calculation;
6
use Spreadsheet\Shared\Font;
7
use Spreadsheet\Shared\StringHelper;
8
use Spreadsheet\Spreadsheet;
9
10
/**
11
 * Spreadsheet_Writer_HTML
12
 *
13
 * Copyright (c) 2006 - 2015 Spreadsheet
14
 *
15
 * This library is free software; you can redistribute it and/or
16
 * modify it under the terms of the GNU Lesser General Public
17
 * License as published by the Free Software Foundation; either
18
 * version 2.1 of the License, or (at your option) any later version.
19
 *
20
 * This library is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
 * Lesser General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Lesser General Public
26
 * License along with this library; if not, write to the Free Software
27
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28
 *
29
 * @category   Spreadsheet
30
 * @copyright  Copyright (c) 2006 - 2015 Spreadsheet (https://github.com/PHPOffice/Spreadsheet)
31
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
32
 * @version    ##VERSION##, ##DATE##
33
 */
34
class HTML extends BaseWriter implements IWriter
35
{
36
    /**
37
     * Spreadsheet object
38
     *
39
     * @var Spreadsheet
40
     */
41
    protected $spreadsheet;
42
43
    /**
44
     * Sheet index to write
45
     *
46
     * @var int
47
     */
48
    private $sheetIndex = 0;
49
50
    /**
51
     * Images root
52
     *
53
     * @var string
54
     */
55
    private $imagesRoot = '.';
56
57
    /**
58
     * embed images, or link to images
59
     *
60
     * @var bool
61
     */
62
    private $embedImages = false;
63
64
    /**
65
     * Use inline CSS?
66
     *
67
     * @var bool
68
     */
69
    private $useInlineCss = false;
70
71
    /**
72
     * Array of CSS styles
73
     *
74
     * @var array
75
     */
76
    private $cssStyles;
77
78
    /**
79
     * Array of column widths in points
80
     *
81
     * @var array
82
     */
83
    private $columnWidths;
84
85
    /**
86
     * Default font
87
     *
88
     * @var \Spreadsheet\Style\Font
89
     */
90
    private $defaultFont;
91
92
    /**
93
     * Flag whether spans have been calculated
94
     *
95
     * @var bool
96
     */
97
    private $spansAreCalculated = false;
98
99
    /**
100
     * Excel cells that should not be written as HTML cells
101
     *
102
     * @var array
103
     */
104
    private $isSpannedCell = [];
105
106
    /**
107
     * Excel cells that are upper-left corner in a cell merge
108
     *
109
     * @var array
110
     */
111
    private $isBaseCell = [];
112
113
    /**
114
     * Excel rows that should not be written as HTML rows
115
     *
116
     * @var array
117
     */
118
    private $isSpannedRow = [];
119
120
    /**
121
     * Is the current writer creating PDF?
122
     *
123
     * @var bool
124
     */
125
    protected $isPdf = false;
126
127
    /**
128
     * Generate the Navigation block
129
     *
130
     * @var bool
131
     */
132
    private $generateSheetNavigationBlock = true;
133
134
    /**
135
     * Create a new Spreadsheet_Writer_HTML
136
     *
137
     * @param    Spreadsheet    $spreadsheet
138
     */
139
    public function __construct(Spreadsheet $spreadsheet)
140
    {
141
        $this->spreadsheet = $spreadsheet;
142
        $this->defaultFont = $this->spreadsheet->getDefaultStyle()->getFont();
143
    }
144
145
    /**
146
     * Save Spreadsheet to file
147
     *
148
     * @param    string        $pFilename
149
     * @throws    \Spreadsheet\Writer\Exception
150
     */
151
    public function save($pFilename = null)
152
    {
153
        // garbage collect
154
        $this->spreadsheet->garbageCollect();
155
156
        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
157
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
158
        $saveArrayReturnType = Calculation::getArrayReturnType();
159
        Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
160
161
        // Build CSS
162
        $this->buildCSS(!$this->useInlineCss);
163
164
        // Open file
165
        $fileHandle = fopen($pFilename, 'wb+');
166
        if ($fileHandle === false) {
167
            throw new \Spreadsheet\Writer\Exception("Could not open file $pFilename for writing.");
168
        }
169
170
        // Write headers
171
        fwrite($fileHandle, $this->generateHTMLHeader(!$this->useInlineCss));
172
173
        // Write navigation (tabs)
174
        if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) {
175
            fwrite($fileHandle, $this->generateNavigation());
176
        }
177
178
        // Write data
179
        fwrite($fileHandle, $this->generateSheetData());
180
181
        // Write footer
182
        fwrite($fileHandle, $this->generateHTMLFooter());
183
184
        // Close file
185
        fclose($fileHandle);
186
187
        Calculation::setArrayReturnType($saveArrayReturnType);
188
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
189
    }
190
191
    /**
192
     * Map VAlign
193
     *
194
     * @param    string        $vAlign        Vertical alignment
195
     * @return string
196
     */
197
    private function mapVAlign($vAlign)
198
    {
199
        switch ($vAlign) {
200
            case \Spreadsheet\Style\Alignment::VERTICAL_BOTTOM:
201
                return 'bottom';
202
            case \Spreadsheet\Style\Alignment::VERTICAL_TOP:
203
                return 'top';
204
            case \Spreadsheet\Style\Alignment::VERTICAL_CENTER:
205
            case \Spreadsheet\Style\Alignment::VERTICAL_JUSTIFY:
206
                return 'middle';
207
            default:
208
                return 'baseline';
209
        }
210
    }
211
212
    /**
213
     * Map HAlign
214
     *
215
     * @param    string        $hAlign        Horizontal alignment
216
     * @return string|false
217
     */
218
    private function mapHAlign($hAlign)
219
    {
220
        switch ($hAlign) {
221
            case \Spreadsheet\Style\Alignment::HORIZONTAL_GENERAL:
222
                return false;
223
            case \Spreadsheet\Style\Alignment::HORIZONTAL_LEFT:
224
                return 'left';
225
            case \Spreadsheet\Style\Alignment::HORIZONTAL_RIGHT:
226
                return 'right';
227
            case \Spreadsheet\Style\Alignment::HORIZONTAL_CENTER:
228
            case \Spreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS:
229
                return 'center';
230
            case \Spreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY:
231
                return 'justify';
232
            default:
233
                return false;
234
        }
235
    }
236
237
    /**
238
     * Map border style
239
     *
240
     * @param    int        $borderStyle        Sheet index
241
     * @return    string
242
     */
243
    private function mapBorderStyle($borderStyle)
244
    {
245
        switch ($borderStyle) {
246
            case \Spreadsheet\Style\Border::BORDER_NONE:
247
                return 'none';
248
            case \Spreadsheet\Style\Border::BORDER_DASHDOT:
249
                return '1px dashed';
250
            case \Spreadsheet\Style\Border::BORDER_DASHDOTDOT:
251
                return '1px dotted';
252
            case \Spreadsheet\Style\Border::BORDER_DASHED:
253
                return '1px dashed';
254
            case \Spreadsheet\Style\Border::BORDER_DOTTED:
255
                return '1px dotted';
256
            case \Spreadsheet\Style\Border::BORDER_DOUBLE:
257
                return '3px double';
258
            case \Spreadsheet\Style\Border::BORDER_HAIR:
259
                return '1px solid';
260
            case \Spreadsheet\Style\Border::BORDER_MEDIUM:
261
                return '2px solid';
262
            case \Spreadsheet\Style\Border::BORDER_MEDIUMDASHDOT:
263
                return '2px dashed';
264
            case \Spreadsheet\Style\Border::BORDER_MEDIUMDASHDOTDOT:
265
                return '2px dotted';
266
            case \Spreadsheet\Style\Border::BORDER_MEDIUMDASHED:
267
                return '2px dashed';
268
            case \Spreadsheet\Style\Border::BORDER_SLANTDASHDOT:
269
                return '2px dashed';
270
            case \Spreadsheet\Style\Border::BORDER_THICK:
271
                return '3px solid';
272
            case \Spreadsheet\Style\Border::BORDER_THIN:
273
                return '1px solid';
274
            default:
275
                // map others to thin
276
                return '1px solid';
277
        }
278
    }
279
280
    /**
281
     * Get sheet index
282
     *
283
     * @return int
284
     */
285
    public function getSheetIndex()
286
    {
287
        return $this->sheetIndex;
288
    }
289
290
    /**
291
     * Set sheet index
292
     *
293
     * @param    int        $pValue        Sheet index
294
     * @return HTML
295
     */
296
    public function setSheetIndex($pValue = 0)
297
    {
298
        $this->sheetIndex = $pValue;
299
300
        return $this;
301
    }
302
303
    /**
304
     * Get sheet index
305
     *
306
     * @return bool
307
     */
308
    public function getGenerateSheetNavigationBlock()
309
    {
310
        return $this->generateSheetNavigationBlock;
311
    }
312
313
    /**
314
     * Set sheet index
315
     *
316
     * @param    bool        $pValue        Flag indicating whether the sheet navigation block should be generated or not
317
     * @return HTML
318
     */
319
    public function setGenerateSheetNavigationBlock($pValue = true)
320
    {
321
        $this->generateSheetNavigationBlock = (bool) $pValue;
322
323
        return $this;
324
    }
325
326
    /**
327
     * Write all sheets (resets sheetIndex to NULL)
328
     */
329
    public function writeAllSheets()
330
    {
331
        $this->sheetIndex = null;
332
333
        return $this;
334
    }
335
336
    /**
337
     * Generate HTML header
338
     *
339
     * @param    bool        $pIncludeStyles        Include styles?
340
     * @throws \Spreadsheet\Writer\Exception
341
     * @return    string
342
     */
343
    public function generateHTMLHeader($pIncludeStyles = false)
344
    {
345
        // Spreadsheet object known?
346
        if (is_null($this->spreadsheet)) {
347
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
348
        }
349
350
        // Construct HTML
351
        $properties = $this->spreadsheet->getProperties();
352
        $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' . PHP_EOL;
353
        $html .= '<!-- Generated by Spreadsheet - https://github.com/PHPOffice/Spreadsheet -->' . PHP_EOL;
354
        $html .= '<html>' . PHP_EOL;
355
        $html .= '  <head>' . PHP_EOL;
356
        $html .= '      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . PHP_EOL;
357
        if ($properties->getTitle() > '') {
358
            $html .= '      <title>' . htmlspecialchars($properties->getTitle()) . '</title>' . PHP_EOL;
359
        }
360
        if ($properties->getCreator() > '') {
361
            $html .= '      <meta name="author" content="' . htmlspecialchars($properties->getCreator()) . '" />' . PHP_EOL;
362
        }
363
        if ($properties->getTitle() > '') {
364
            $html .= '      <meta name="title" content="' . htmlspecialchars($properties->getTitle()) . '" />' . PHP_EOL;
365
        }
366 View Code Duplication
        if ($properties->getDescription() > '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
367
            $html .= '      <meta name="description" content="' . htmlspecialchars($properties->getDescription()) . '" />' . PHP_EOL;
368
        }
369
        if ($properties->getSubject() > '') {
370
            $html .= '      <meta name="subject" content="' . htmlspecialchars($properties->getSubject()) . '" />' . PHP_EOL;
371
        }
372
        if ($properties->getKeywords() > '') {
373
            $html .= '      <meta name="keywords" content="' . htmlspecialchars($properties->getKeywords()) . '" />' . PHP_EOL;
374
        }
375
        if ($properties->getCategory() > '') {
376
            $html .= '      <meta name="category" content="' . htmlspecialchars($properties->getCategory()) . '" />' . PHP_EOL;
377
        }
378 View Code Duplication
        if ($properties->getCompany() > '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
            $html .= '      <meta name="company" content="' . htmlspecialchars($properties->getCompany()) . '" />' . PHP_EOL;
380
        }
381 View Code Duplication
        if ($properties->getManager() > '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
382
            $html .= '      <meta name="manager" content="' . htmlspecialchars($properties->getManager()) . '" />' . PHP_EOL;
383
        }
384
385
        if ($pIncludeStyles) {
386
            $html .= $this->generateStyles(true);
387
        }
388
389
        $html .= '  </head>' . PHP_EOL;
390
        $html .= '' . PHP_EOL;
391
        $html .= '  <body>' . PHP_EOL;
392
393
        return $html;
394
    }
395
396
    /**
397
     * Generate sheet data
398
     *
399
     * @throws \Spreadsheet\Writer\Exception
400
     * @return    string
401
     */
402
    public function generateSheetData()
403
    {
404
        // Spreadsheet object known?
405
        if (is_null($this->spreadsheet)) {
406
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
407
        }
408
409
        // Ensure that Spans have been calculated?
410
        if ($this->sheetIndex !== null || !$this->spansAreCalculated) {
411
            $this->calculateSpans();
412
        }
413
414
        // Fetch sheets
415
        $sheets = [];
416 View Code Duplication
        if (is_null($this->sheetIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
417
            $sheets = $this->spreadsheet->getAllSheets();
418
        } else {
419
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
420
        }
421
422
        // Construct HTML
423
        $html = '';
424
425
        // Loop all sheets
426
        $sheetId = 0;
427
        foreach ($sheets as $sheet) {
428
            // Write table header
429
            $html .= $this->generateTableHeader($sheet);
430
431
            // Get worksheet dimension
432
            $dimension = explode(':', $sheet->calculateWorksheetDimension());
433
            $dimension[0] = \Spreadsheet\Cell::coordinateFromString($dimension[0]);
434
            $dimension[0][0] = \Spreadsheet\Cell::columnIndexFromString($dimension[0][0]) - 1;
435
            $dimension[1] = \Spreadsheet\Cell::coordinateFromString($dimension[1]);
436
            $dimension[1][0] = \Spreadsheet\Cell::columnIndexFromString($dimension[1][0]) - 1;
437
438
            // row min,max
439
            $rowMin = $dimension[0][1];
440
            $rowMax = $dimension[1][1];
441
442
            // calculate start of <tbody>, <thead>
443
            $tbodyStart = $rowMin;
444
            $theadStart = $theadEnd = 0; // default: no <thead>    no </thead>
445
            if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
446
                $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop();
447
448
                // we can only support repeating rows that start at top row
449
                if ($rowsToRepeatAtTop[0] == 1) {
450
                    $theadStart = $rowsToRepeatAtTop[0];
451
                    $theadEnd = $rowsToRepeatAtTop[1];
452
                    $tbodyStart = $rowsToRepeatAtTop[1] + 1;
453
                }
454
            }
455
456
            // Loop through cells
457
            $row = $rowMin - 1;
458
            while ($row++ < $rowMax) {
459
                // <thead> ?
460
                if ($row == $theadStart) {
461
                    $html .= '        <thead>' . PHP_EOL;
462
                    $cellType = 'th';
463
                }
464
465
                // <tbody> ?
466
                if ($row == $tbodyStart) {
467
                    $html .= '        <tbody>' . PHP_EOL;
468
                    $cellType = 'td';
469
                }
470
471
                // Write row if there are HTML table cells in it
472
                if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
473
                    // Start a new rowData
474
                    $rowData = [];
475
                    // Loop through columns
476
                    $column = $dimension[0][0] - 1;
477
                    while ($column++ < $dimension[1][0]) {
478
                        // Cell exists?
479
                        if ($sheet->cellExistsByColumnAndRow($column, $row)) {
480
                            $rowData[$column] = \Spreadsheet\Cell::stringFromColumnIndex($column) . $row;
481
                        } else {
482
                            $rowData[$column] = '';
483
                        }
484
                    }
485
                    $html .= $this->generateRow($sheet, $rowData, $row - 1, $cellType);
0 ignored issues
show
Bug introduced by
The variable $cellType does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
486
                }
487
488
                // </thead> ?
489
                if ($row == $theadEnd) {
490
                    $html .= '        </thead>' . PHP_EOL;
491
                }
492
            }
493
            $html .= $this->extendRowsForChartsAndImages($sheet, $row);
494
495
            // Close table body.
496
            $html .= '        </tbody>' . PHP_EOL;
497
498
            // Write table footer
499
            $html .= $this->generateTableFooter();
500
501
            // Writing PDF?
502
            if ($this->isPdf) {
503
                if (is_null($this->sheetIndex) && $sheetId + 1 < $this->spreadsheet->getSheetCount()) {
504
                    $html .= '<div style="page-break-before:always" />';
505
                }
506
            }
507
508
            // Next sheet
509
            ++$sheetId;
510
        }
511
512
        return $html;
513
    }
514
515
    /**
516
     * Generate sheet tabs
517
     *
518
     * @throws \Spreadsheet\Writer\Exception
519
     * @return    string
520
     */
521
    public function generateNavigation()
522
    {
523
        // Spreadsheet object known?
524
        if (is_null($this->spreadsheet)) {
525
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
526
        }
527
528
        // Fetch sheets
529
        $sheets = [];
530 View Code Duplication
        if (is_null($this->sheetIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
531
            $sheets = $this->spreadsheet->getAllSheets();
532
        } else {
533
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
534
        }
535
536
        // Construct HTML
537
        $html = '';
538
539
        // Only if there are more than 1 sheets
540
        if (count($sheets) > 1) {
541
            // Loop all sheets
542
            $sheetId = 0;
543
544
            $html .= '<ul class="navigation">' . PHP_EOL;
545
546
            foreach ($sheets as $sheet) {
547
                $html .= '  <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
548
                ++$sheetId;
549
            }
550
551
            $html .= '</ul>' . PHP_EOL;
552
        }
553
554
        return $html;
555
    }
556
557
    private function extendRowsForChartsAndImages(\Spreadsheet\Worksheet $pSheet, $row)
558
    {
559
        $rowMax = $row;
560
        $colMax = 'A';
561
        if ($this->includeCharts) {
562
            foreach ($pSheet->getChartCollection() as $chart) {
563
                if ($chart instanceof Spreadsheet_Chart) {
0 ignored issues
show
Bug introduced by
The class PhpSpreadsheet\Writer\Spreadsheet_Chart does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
564
                    $chartCoordinates = $chart->getTopLeftPosition();
565
                    $chartTL = \Spreadsheet\Cell::coordinateFromString($chartCoordinates['cell']);
566
                    $chartCol = \Spreadsheet\Cell::columnIndexFromString($chartTL[0]);
567
                    if ($chartTL[1] > $rowMax) {
568
                        $rowMax = $chartTL[1];
569
                        if ($chartCol > \Spreadsheet\Cell::columnIndexFromString($colMax)) {
570
                            $colMax = $chartTL[0];
571
                        }
572
                    }
573
                }
574
            }
575
        }
576
577
        foreach ($pSheet->getDrawingCollection() as $drawing) {
578
            if ($drawing instanceof \Spreadsheet\Worksheet\Drawing) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Worksheet\Drawing does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
579
                $imageTL = \Spreadsheet\Cell::coordinateFromString($drawing->getCoordinates());
580
                $imageCol = \Spreadsheet\Cell::columnIndexFromString($imageTL[0]);
581
                if ($imageTL[1] > $rowMax) {
582
                    $rowMax = $imageTL[1];
583
                    if ($imageCol > \Spreadsheet\Cell::columnIndexFromString($colMax)) {
584
                        $colMax = $imageTL[0];
585
                    }
586
                }
587
            }
588
        }
589
        $html = '';
590
        ++$colMax;
591
        while ($row <= $rowMax) {
592
            $html .= '<tr>';
593
            for ($col = 'A'; $col != $colMax; ++$col) {
594
                $html .= '<td>';
595
                $html .= $this->writeImageInCell($pSheet, $col . $row);
596
                if ($this->includeCharts) {
597
                    $html .= $this->writeChartInCell($pSheet, $col . $row);
598
                }
599
                $html .= '</td>';
600
            }
601
            ++$row;
602
            $html .= '</tr>';
603
        }
604
605
        return $html;
606
    }
607
608
    /**
609
     * Generate image tag in cell
610
     *
611
     * @param    \Spreadsheet\Worksheet    $pSheet            \Spreadsheet\Worksheet
612
     * @param    string                $coordinates    Cell coordinates
613
     * @throws    \Spreadsheet\Writer\Exception
614
     * @return    string
615
     */
616
    private function writeImageInCell(\Spreadsheet\Worksheet $pSheet, $coordinates)
617
    {
618
        // Construct HTML
619
        $html = '';
620
621
        // Write images
622
        foreach ($pSheet->getDrawingCollection() as $drawing) {
623
            if ($drawing instanceof \Spreadsheet\Worksheet\Drawing) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Worksheet\Drawing does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
624
                if ($drawing->getCoordinates() == $coordinates) {
625
                    $filename = $drawing->getPath();
626
627
                    // Strip off eventual '.'
628
                    if (substr($filename, 0, 1) == '.') {
629
                        $filename = substr($filename, 1);
630
                    }
631
632
                    // Prepend images root
633
                    $filename = $this->getImagesRoot() . $filename;
634
635
                    // Strip off eventual '.'
636
                    if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') {
637
                        $filename = substr($filename, 1);
638
                    }
639
640
                    // Convert UTF8 data to PCDATA
641
                    $filename = htmlspecialchars($filename);
642
643
                    $html .= PHP_EOL;
644
                    if ((!$this->embedImages) || ($this->isPdf)) {
645
                        $imageData = $filename;
646
                    } else {
647
                        $imageDetails = getimagesize($filename);
648
                        if ($fp = fopen($filename, 'rb', 0)) {
649
                            $picture = fread($fp, filesize($filename));
650
                            fclose($fp);
651
                            // base64 encode the binary data, then break it
652
                            // into chunks according to RFC 2045 semantics
653
                            $base64 = chunk_split(base64_encode($picture));
654
                            $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
655
                        } else {
656
                            $imageData = $filename;
657
                        }
658
                    }
659
660
                    $html .= '<div style="position: relative;">';
661
                    $html .= '<img style="position: absolute; z-index: 1; left: ' .
662
                        $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: ' .
663
                        $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="' .
664
                        $imageData . '" border="0" />';
665
                    $html .= '</div>';
666
                }
667
            } elseif ($drawing instanceof \Spreadsheet\Worksheet\MemoryDrawing) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Worksheet\MemoryDrawing does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
668
                if ($drawing->getCoordinates() != $coordinates) {
669
                    continue;
670
                }
671
                ob_start();                                //  Let's start output buffering.
672
                imagepng($drawing->getImageResource());    //  This will normally output the image, but because of ob_start(), it won't.
673
                $contents = ob_get_contents();             //  Instead, output above is saved to $contents
674
                ob_end_clean();                            //  End the output buffer.
675
676
                $dataUri = 'data:image/jpeg;base64,' . base64_encode($contents);
677
678
                //  Because of the nature of tables, width is more important than height.
679
                //  max-width: 100% ensures that image doesnt overflow containing cell
680
                //  width: X sets width of supplied image.
681
                //  As a result, images bigger than cell will be contained and images smaller will not get stretched
682
                $html .= '<img src="' . $dataUri . '" style="max-width:100%;width:' . $drawing->getWidth() . 'px;" />';
683
            }
684
        }
685
686
        return $html;
687
    }
688
689
    /**
690
     * Generate chart tag in cell
691
     *
692
     * @param    \Spreadsheet\Worksheet    $pSheet            \Spreadsheet\Worksheet
693
     * @param    string                $coordinates    Cell coordinates
694
     * @throws    \Spreadsheet\Writer\Exception
695
     * @return    string
696
     */
697
    private function writeChartInCell(\Spreadsheet\Worksheet $pSheet, $coordinates)
698
    {
699
        // Construct HTML
700
        $html = '';
701
702
        // Write charts
703
        foreach ($pSheet->getChartCollection() as $chart) {
704
            if ($chart instanceof Spreadsheet_Chart) {
0 ignored issues
show
Bug introduced by
The class PhpSpreadsheet\Writer\Spreadsheet_Chart does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
705
                $chartCoordinates = $chart->getTopLeftPosition();
706
                if ($chartCoordinates['cell'] == $coordinates) {
707
                    $chartFileName = \Spreadsheet\Shared\File::sysGetTempDir() . '/' . uniqid() . '.png';
708
                    if (!$chart->render($chartFileName)) {
709
                        return;
710
                    }
711
712
                    $html .= PHP_EOL;
713
                    $imageDetails = getimagesize($chartFileName);
714
                    if ($fp = fopen($chartFileName, 'rb', 0)) {
715
                        $picture = fread($fp, filesize($chartFileName));
716
                        fclose($fp);
717
                        // base64 encode the binary data, then break it
718
                        // into chunks according to RFC 2045 semantics
719
                        $base64 = chunk_split(base64_encode($picture));
720
                        $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
721
722
                        $html .= '<div style="position: relative;">';
723
                        $html .= '<img style="position: absolute; z-index: 1; left: ' . $chartCoordinates['xOffset'] . 'px; top: ' . $chartCoordinates['yOffset'] . 'px; width: ' . $imageDetails[0] . 'px; height: ' . $imageDetails[1] . 'px;" src="' . $imageData . '" border="0" />' . PHP_EOL;
724
                        $html .= '</div>';
725
726
                        unlink($chartFileName);
727
                    }
728
                }
729
            }
730
        }
731
732
        // Return
733
        return $html;
734
    }
735
736
    /**
737
     * Generate CSS styles
738
     *
739
     * @param    bool    $generateSurroundingHTML    Generate surrounding HTML tags? (&lt;style&gt; and &lt;/style&gt;)
740
     * @throws    \Spreadsheet\Writer\Exception
741
     * @return    string
742
     */
743
    public function generateStyles($generateSurroundingHTML = true)
744
    {
745
        // Spreadsheet object known?
746
        if (is_null($this->spreadsheet)) {
747
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
748
        }
749
750
        // Build CSS
751
        $css = $this->buildCSS($generateSurroundingHTML);
752
753
        // Construct HTML
754
        $html = '';
755
756
        // Start styles
757
        if ($generateSurroundingHTML) {
758
            $html .= '    <style type="text/css">' . PHP_EOL;
759
            $html .= '      html { ' . $this->assembleCSS($css['html']) . ' }' . PHP_EOL;
760
        }
761
762
        // Write all other styles
763
        foreach ($css as $styleName => $styleDefinition) {
764
            if ($styleName != 'html') {
765
                $html .= '      ' . $styleName . ' { ' . $this->assembleCSS($styleDefinition) . ' }' . PHP_EOL;
766
            }
767
        }
768
769
        // End styles
770
        if ($generateSurroundingHTML) {
771
            $html .= '    </style>' . PHP_EOL;
772
        }
773
774
        // Return
775
        return $html;
776
    }
777
778
    /**
779
     * Build CSS styles
780
     *
781
     * @param    bool    $generateSurroundingHTML    Generate surrounding HTML style? (html { })
782
     * @throws    \Spreadsheet\Writer\Exception
783
     * @return    array
784
     */
785
    public function buildCSS($generateSurroundingHTML = true)
786
    {
787
        // Spreadsheet object known?
788
        if (is_null($this->spreadsheet)) {
789
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
790
        }
791
792
        // Cached?
793
        if (!is_null($this->cssStyles)) {
794
            return $this->cssStyles;
795
        }
796
797
        // Ensure that spans have been calculated
798
        if (!$this->spansAreCalculated) {
799
            $this->calculateSpans();
800
        }
801
802
        // Construct CSS
803
        $css = [];
804
805
        // Start styles
806
        if ($generateSurroundingHTML) {
807
            // html { }
808
            $css['html']['font-family'] = 'Calibri, Arial, Helvetica, sans-serif';
809
            $css['html']['font-size'] = '11pt';
810
            $css['html']['background-color'] = 'white';
811
        }
812
813
        // table { }
814
        $css['table']['border-collapse'] = 'collapse';
815
        if (!$this->isPdf) {
816
            $css['table']['page-break-after'] = 'always';
817
        }
818
819
        // .gridlines td { }
820
        $css['.gridlines td']['border'] = '1px dotted black';
821
        $css['.gridlines th']['border'] = '1px dotted black';
822
823
        // .b {}
824
        $css['.b']['text-align'] = 'center'; // BOOL
825
826
        // .e {}
827
        $css['.e']['text-align'] = 'center'; // ERROR
828
829
        // .f {}
830
        $css['.f']['text-align'] = 'right'; // FORMULA
831
832
        // .inlineStr {}
833
        $css['.inlineStr']['text-align'] = 'left'; // INLINE
834
835
        // .n {}
836
        $css['.n']['text-align'] = 'right'; // NUMERIC
837
838
        // .s {}
839
        $css['.s']['text-align'] = 'left'; // STRING
840
841
        // Calculate cell style hashes
842
        foreach ($this->spreadsheet->getCellXfCollection() as $index => $style) {
843
            $css['td.style' . $index] = $this->createCSSStyle($style);
844
            $css['th.style' . $index] = $this->createCSSStyle($style);
845
        }
846
847
        // Fetch sheets
848
        $sheets = [];
849 View Code Duplication
        if (is_null($this->sheetIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
850
            $sheets = $this->spreadsheet->getAllSheets();
851
        } else {
852
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
853
        }
854
855
        // Build styles per sheet
856
        foreach ($sheets as $sheet) {
857
            // Calculate hash code
858
            $sheetIndex = $sheet->getParent()->getIndex($sheet);
859
860
            // Build styles
861
            // Calculate column widths
862
            $sheet->calculateColumnWidths();
863
864
            // col elements, initialize
865
            $highestColumnIndex = \Spreadsheet\Cell::columnIndexFromString($sheet->getHighestColumn()) - 1;
866
            $column = -1;
867
            while ($column++ < $highestColumnIndex) {
868
                $this->columnWidths[$sheetIndex][$column] = 42; // approximation
869
                $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
870
            }
871
872
            // col elements, loop through columnDimensions and set width
873
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
874
                if (($width = \Spreadsheet\Shared\Drawing::cellDimensionToPixels($columnDimension->getWidth(), $this->defaultFont)) >= 0) {
875
                    $width = \Spreadsheet\Shared\Drawing::pixelsToPoints($width);
876
                    $column = \Spreadsheet\Cell::columnIndexFromString($columnDimension->getColumnIndex()) - 1;
877
                    $this->columnWidths[$sheetIndex][$column] = $width;
878
                    $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = $width . 'pt';
879
880 View Code Duplication
                    if ($columnDimension->getVisible() === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
881
                        $css['table.sheet' . $sheetIndex . ' col.col' . $column]['visibility'] = 'collapse';
882
                        $css['table.sheet' . $sheetIndex . ' col.col' . $column]['*display'] = 'none'; // target IE6+7
883
                    }
884
                }
885
            }
886
887
            // Default row height
888
            $rowDimension = $sheet->getDefaultRowDimension();
889
890
            // table.sheetN tr { }
891
            $css['table.sheet' . $sheetIndex . ' tr'] = [];
892
893 View Code Duplication
            if ($rowDimension->getRowHeight() == -1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
894
                $pt_height = Font::getDefaultRowHeightByFont($this->spreadsheet->getDefaultStyle()->getFont());
895
            } else {
896
                $pt_height = $rowDimension->getRowHeight();
897
            }
898
            $css['table.sheet' . $sheetIndex . ' tr']['height'] = $pt_height . 'pt';
899
            if ($rowDimension->getVisible() === false) {
900
                $css['table.sheet' . $sheetIndex . ' tr']['display'] = 'none';
901
                $css['table.sheet' . $sheetIndex . ' tr']['visibility'] = 'hidden';
902
            }
903
904
            // Calculate row heights
905
            foreach ($sheet->getRowDimensions() as $rowDimension) {
906
                $row = $rowDimension->getRowIndex() - 1;
907
908
                // table.sheetN tr.rowYYYYYY { }
909
                $css['table.sheet' . $sheetIndex . ' tr.row' . $row] = [];
910
911 View Code Duplication
                if ($rowDimension->getRowHeight() == -1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
912
                    $pt_height = Font::getDefaultRowHeightByFont($this->spreadsheet->getDefaultStyle()->getFont());
913
                } else {
914
                    $pt_height = $rowDimension->getRowHeight();
915
                }
916
                $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['height'] = $pt_height . 'pt';
917 View Code Duplication
                if ($rowDimension->getVisible() === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
918
                    $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['display'] = 'none';
919
                    $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['visibility'] = 'hidden';
920
                }
921
            }
922
        }
923
924
        // Cache
925
        if (is_null($this->cssStyles)) {
926
            $this->cssStyles = $css;
927
        }
928
929
        // Return
930
        return $css;
931
    }
932
933
    /**
934
     * Create CSS style
935
     *
936
     * @param    \Spreadsheet\Style        $pStyle            Spreadsheet_Style
937
     * @return    array
938
     */
939
    private function createCSSStyle(\Spreadsheet\Style $pStyle)
940
    {
941
        // Construct CSS
942
        $css = '';
0 ignored issues
show
Unused Code introduced by
$css is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
943
944
        // Create CSS
945
        $css = array_merge(
946
            $this->createCSSStyleAlignment($pStyle->getAlignment()),
947
            $this->createCSSStyleBorders($pStyle->getBorders()),
948
            $this->createCSSStyleFont($pStyle->getFont()),
949
            $this->createCSSStyleFill($pStyle->getFill())
950
        );
951
952
        // Return
953
        return $css;
954
    }
955
956
    /**
957
     * Create CSS style (\Spreadsheet\Style\Alignment)
958
     *
959
     * @param    \Spreadsheet\Style\Alignment        $pStyle            \Spreadsheet\Style\Alignment
960
     * @return    array
961
     */
962
    private function createCSSStyleAlignment(\Spreadsheet\Style\Alignment $pStyle)
963
    {
964
        // Construct CSS
965
        $css = [];
966
967
        // Create CSS
968
        $css['vertical-align'] = $this->mapVAlign($pStyle->getVertical());
969
        if ($textAlign = $this->mapHAlign($pStyle->getHorizontal())) {
970
            $css['text-align'] = $textAlign;
971
            if (in_array($textAlign, ['left', 'right'])) {
972
                $css['padding-' . $textAlign] = (string) ((int) $pStyle->getIndent() * 9) . 'px';
973
            }
974
        }
975
976
        return $css;
977
    }
978
979
    /**
980
     * Create CSS style (\Spreadsheet\Style\Font)
981
     *
982
     * @param    \Spreadsheet\Style\Font        $pStyle            \Spreadsheet\Style\Font
983
     * @return    array
984
     */
985
    private function createCSSStyleFont(\Spreadsheet\Style\Font $pStyle)
986
    {
987
        // Construct CSS
988
        $css = [];
989
990
        // Create CSS
991
        if ($pStyle->getBold()) {
992
            $css['font-weight'] = 'bold';
993
        }
994
        if ($pStyle->getUnderline() != \Spreadsheet\Style\Font::UNDERLINE_NONE && $pStyle->getStrikethrough()) {
995
            $css['text-decoration'] = 'underline line-through';
996
        } elseif ($pStyle->getUnderline() != \Spreadsheet\Style\Font::UNDERLINE_NONE) {
997
            $css['text-decoration'] = 'underline';
998
        } elseif ($pStyle->getStrikethrough()) {
999
            $css['text-decoration'] = 'line-through';
1000
        }
1001
        if ($pStyle->getItalic()) {
1002
            $css['font-style'] = 'italic';
1003
        }
1004
1005
        $css['color'] = '#' . $pStyle->getColor()->getRGB();
1006
        $css['font-family'] = '\'' . $pStyle->getName() . '\'';
1007
        $css['font-size'] = $pStyle->getSize() . 'pt';
1008
1009
        return $css;
1010
    }
1011
1012
    /**
1013
     * Create CSS style (\Spreadsheet\Style\Borders)
1014
     *
1015
     * @param    \Spreadsheet\Style\Borders        $pStyle            \Spreadsheet\Style\Borders
1016
     * @return    array
1017
     */
1018
    private function createCSSStyleBorders(\Spreadsheet\Style\Borders $pStyle)
1019
    {
1020
        // Construct CSS
1021
        $css = [];
1022
1023
        // Create CSS
1024
        $css['border-bottom'] = $this->createCSSStyleBorder($pStyle->getBottom());
1025
        $css['border-top'] = $this->createCSSStyleBorder($pStyle->getTop());
1026
        $css['border-left'] = $this->createCSSStyleBorder($pStyle->getLeft());
1027
        $css['border-right'] = $this->createCSSStyleBorder($pStyle->getRight());
1028
1029
        return $css;
1030
    }
1031
1032
    /**
1033
     * Create CSS style (\Spreadsheet\Style\Border)
1034
     *
1035
     * @param    \Spreadsheet\Style\Border        $pStyle            \Spreadsheet\Style\Border
1036
     * @return    string
1037
     */
1038
    private function createCSSStyleBorder(\Spreadsheet\Style\Border $pStyle)
1039
    {
1040
        // Create CSS
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% 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...
1041
//        $css = $this->mapBorderStyle($pStyle->getBorderStyle()) . ' #' . $pStyle->getColor()->getRGB();
1042
        //    Create CSS - add !important to non-none border styles for merged cells
1043
        $borderStyle = $this->mapBorderStyle($pStyle->getBorderStyle());
1044
        $css = $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
1045
1046
        return $css;
1047
    }
1048
1049
    /**
1050
     * Create CSS style (\Spreadsheet\Style\Fill)
1051
     *
1052
     * @param    \Spreadsheet\Style\Fill        $pStyle            \Spreadsheet\Style\Fill
1053
     * @return    array
1054
     */
1055
    private function createCSSStyleFill(\Spreadsheet\Style\Fill $pStyle)
1056
    {
1057
        // Construct HTML
1058
        $css = [];
1059
1060
        // Create CSS
1061
        $value = $pStyle->getFillType() == \Spreadsheet\Style\Fill::FILL_NONE ?
1062
            'white' : '#' . $pStyle->getStartColor()->getRGB();
1063
        $css['background-color'] = $value;
1064
1065
        return $css;
1066
    }
1067
1068
    /**
1069
     * Generate HTML footer
1070
     */
1071
    public function generateHTMLFooter()
1072
    {
1073
        // Construct HTML
1074
        $html = '';
1075
        $html .= '  </body>' . PHP_EOL;
1076
        $html .= '</html>' . PHP_EOL;
1077
1078
        return $html;
1079
    }
1080
1081
    /**
1082
     * Generate table header
1083
     *
1084
     * @param    \Spreadsheet\Worksheet    $pSheet        The worksheet for the table we are writing
1085
     * @throws    \Spreadsheet\Writer\Exception
1086
     * @return    string
1087
     */
1088
    private function generateTableHeader($pSheet)
1089
    {
1090
        $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
1091
1092
        // Construct HTML
1093
        $html = '';
1094
        $html .= $this->setMargins($pSheet);
1095
1096
        if (!$this->useInlineCss) {
1097
            $gridlines = $pSheet->getShowGridlines() ? ' gridlines' : '';
1098
            $html .= '    <table border="0" cellpadding="0" cellspacing="0" id="sheet' . $sheetIndex . '" class="sheet' . $sheetIndex . $gridlines . '">' . PHP_EOL;
1099
        } else {
1100
            $style = isset($this->cssStyles['table']) ?
1101
                $this->assembleCSS($this->cssStyles['table']) : '';
1102
1103
            if ($this->isPdf && $pSheet->getShowGridlines()) {
1104
                $html .= '    <table border="1" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="1" style="' . $style . '">' . PHP_EOL;
1105
            } else {
1106
                $html .= '    <table border="0" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="0" style="' . $style . '">' . PHP_EOL;
1107
            }
1108
        }
1109
1110
        // Write <col> elements
1111
        $highestColumnIndex = \Spreadsheet\Cell::columnIndexFromString($pSheet->getHighestColumn()) - 1;
1112
        $i = -1;
1113
        while ($i++ < $highestColumnIndex) {
1114 View Code Duplication
            if (!$this->isPdf) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1115
                if (!$this->useInlineCss) {
1116
                    $html .= '        <col class="col' . $i . '">' . PHP_EOL;
1117
                } else {
1118
                    $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) ?
1119
                        $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
1120
                    $html .= '        <col style="' . $style . '">' . PHP_EOL;
1121
                }
1122
            }
1123
        }
1124
1125
        return $html;
1126
    }
1127
1128
    /**
1129
     * Generate table footer
1130
     *
1131
     * @throws    \Spreadsheet\Writer\Exception
1132
     */
1133
    private function generateTableFooter()
1134
    {
1135
        $html = '    </table>' . PHP_EOL;
1136
1137
        return $html;
1138
    }
1139
1140
    /**
1141
     * Generate row
1142
     *
1143
     * @param    \Spreadsheet\Worksheet    $pSheet            \Spreadsheet\Worksheet
1144
     * @param    array                $pValues        Array containing cells in a row
1145
     * @param    int                    $pRow            Row number (0-based)
1146
     * @throws    \Spreadsheet\Writer\Exception
1147
     * @return    string
1148
     */
1149
    private function generateRow(\Spreadsheet\Worksheet $pSheet, $pValues = null, $pRow = 0, $cellType = 'td')
1150
    {
1151
        if (is_array($pValues)) {
1152
            // Construct HTML
1153
            $html = '';
1154
1155
            // Sheet index
1156
            $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
1157
1158
            // DomPDF and breaks
1159
            if ($this->isPdf && count($pSheet->getBreaks()) > 0) {
1160
                $breaks = $pSheet->getBreaks();
1161
1162
                // check if a break is needed before this row
1163
                if (isset($breaks['A' . $pRow])) {
1164
                    // close table: </table>
1165
                    $html .= $this->generateTableFooter();
1166
1167
                    // insert page break
1168
                    $html .= '<div style="page-break-before:always" />';
1169
1170
                    // open table again: <table> + <col> etc.
1171
                    $html .= $this->generateTableHeader($pSheet);
1172
                }
1173
            }
1174
1175
            // Write row start
1176 View Code Duplication
            if (!$this->useInlineCss) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1177
                $html .= '          <tr class="row' . $pRow . '">' . PHP_EOL;
1178
            } else {
1179
                $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
1180
                    ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
1181
1182
                $html .= '          <tr style="' . $style . '">' . PHP_EOL;
1183
            }
1184
1185
            // Write cells
1186
            $colNum = 0;
1187
            foreach ($pValues as $cellAddress) {
1188
                $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : '';
1189
                $coordinate = \Spreadsheet\Cell::stringFromColumnIndex($colNum) . ($pRow + 1);
1190
                if (!$this->useInlineCss) {
1191
                    $cssClass = '';
0 ignored issues
show
Unused Code introduced by
$cssClass is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1192
                    $cssClass = 'column' . $colNum;
1193
                } else {
1194
                    $cssClass = [];
1195
                    if ($cellType == 'th') {
1196 View Code Duplication
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1197
                            $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum];
1198
                        }
1199 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1200
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
1201
                            $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
1202
                        }
1203
                    }
1204
                }
1205
                $colSpan = 1;
1206
                $rowSpan = 1;
1207
1208
                // initialize
1209
                $cellData = '&nbsp;';
1210
1211
                // \Spreadsheet\Cell
1212
                if ($cell instanceof \Spreadsheet\Cell) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Cell does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1213
                    $cellData = '';
1214
                    if (is_null($cell->getParent())) {
1215
                        $cell->attach($pSheet);
1216
                    }
1217
                    // Value
1218
                    if ($cell->getValue() instanceof \Spreadsheet\RichText) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\RichText does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1219
                        // Loop through rich text elements
1220
                        $elements = $cell->getValue()->getRichTextElements();
1221
                        foreach ($elements as $element) {
1222
                            // Rich text start?
1223
                            if ($element instanceof \Spreadsheet\RichText\Run) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\RichText\Run does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1224
                                $cellData .= '<span style="' . $this->assembleCSS($this->createCSSStyleFont($element->getFont())) . '">';
1225
1226
                                if ($element->getFont()->getSuperScript()) {
1227
                                    $cellData .= '<sup>';
1228
                                } elseif ($element->getFont()->getSubScript()) {
1229
                                    $cellData .= '<sub>';
1230
                                }
1231
                            }
1232
1233
                            // Convert UTF8 data to PCDATA
1234
                            $cellText = $element->getText();
1235
                            $cellData .= htmlspecialchars($cellText);
1236
1237
                            if ($element instanceof \Spreadsheet\RichText\Run) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\RichText\Run does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1238
                                if ($element->getFont()->getSuperScript()) {
1239
                                    $cellData .= '</sup>';
1240
                                } elseif ($element->getFont()->getSubScript()) {
1241
                                    $cellData .= '</sub>';
1242
                                }
1243
1244
                                $cellData .= '</span>';
1245
                            }
1246
                        }
1247
                    } else {
1248
                        if ($this->preCalculateFormulas) {
1249
                            $cellData = \Spreadsheet\Style\NumberFormat::toFormattedString(
1250
                                $cell->getCalculatedValue(),
1251
                                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
1252
                                [$this, 'formatColor']
1253
                            );
1254
                        } else {
1255
                            $cellData = \Spreadsheet\Style\NumberFormat::toFormattedString(
1256
                                $cell->getValue(),
1257
                                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
1258
                                [$this, 'formatColor']
1259
                            );
1260
                        }
1261
                        $cellData = htmlspecialchars($cellData);
1262
                        if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperScript()) {
1263
                            $cellData = '<sup>' . $cellData . '</sup>';
1264
                        } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubScript()) {
1265
                            $cellData = '<sub>' . $cellData . '</sub>';
1266
                        }
1267
                    }
1268
1269
                    // Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp;
1270
                    // Example: "  Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world"
1271
                    $cellData = preg_replace('/(?m)(?:^|\\G) /', '&nbsp;', $cellData);
1272
1273
                    // convert newline "\n" to '<br>'
1274
                    $cellData = nl2br($cellData);
1275
1276
                    // Extend CSS class?
1277
                    if (!$this->useInlineCss) {
1278
                        $cssClass .= ' style' . $cell->getXfIndex();
1279
                        $cssClass .= ' ' . $cell->getDataType();
1280
                    } else {
1281
                        if ($cellType == 'th') {
1282 View Code Duplication
                            if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1283
                                $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
1284
                            }
1285 View Code Duplication
                        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1286
                            if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
1287
                                $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
1288
                            }
1289
                        }
1290
1291
                        // General horizontal alignment: Actual horizontal alignment depends on dataType
1292
                        $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex());
1293
                        if ($sharedStyle->getAlignment()->getHorizontal() == \Spreadsheet\Style\Alignment::HORIZONTAL_GENERAL
1294
                            && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])) {
1295
                            $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align'];
1296
                        }
1297
                    }
1298
                }
1299
1300
                // Hyperlink?
1301
                if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
1302
                    $cellData = '<a href="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getUrl()) . '" title="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getTooltip()) . '">' . $cellData . '</a>';
1303
                }
1304
1305
                // Should the cell be written or is it swallowed by a rowspan or colspan?
1306
                $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
1307
                            && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]);
1308
1309
                // Colspan and Rowspan
1310
                $colspan = 1;
0 ignored issues
show
Unused Code introduced by
$colspan is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1311
                $rowspan = 1;
0 ignored issues
show
Unused Code introduced by
$rowspan is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1312
                if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
1313
                    $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
1314
                    $rowSpan = $spans['rowspan'];
1315
                    $colSpan = $spans['colspan'];
1316
1317
                    //    Also apply style from last cell in merge to fix borders -
1318
                    //        relies on !important for non-none border declarations in createCSSStyleBorder
1319
                    $endCellCoord = \Spreadsheet\Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan);
1320
                    if (!$this->useInlineCss) {
1321
                        $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex();
1322
                    }
1323
                }
1324
1325
                // Write
1326
                if ($writeCell) {
1327
                    // Column start
1328
                    $html .= '            <' . $cellType;
1329
                    if (!$this->useInlineCss) {
1330
                        $html .= ' class="' . $cssClass . '"';
1331
                    } else {
1332
                        //** Necessary redundant code for the sake of Spreadsheet_Writer_PDF **
1333
                        // We must explicitly write the width of the <td> element because TCPDF
1334
                        // does not recognize e.g. <col style="width:42pt">
1335
                        $width = 0;
1336
                        $i = $colNum - 1;
1337
                        $e = $colNum + $colSpan - 1;
1338
                        while ($i++ < $e) {
1339
                            if (isset($this->columnWidths[$sheetIndex][$i])) {
1340
                                $width += $this->columnWidths[$sheetIndex][$i];
1341
                            }
1342
                        }
1343
                        $cssClass['width'] = $width . 'pt';
1344
1345
                        // We must also explicitly write the height of the <td> element because TCPDF
1346
                        // does not recognize e.g. <tr style="height:50pt">
1347
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
1348
                            $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
1349
                            $cssClass['height'] = $height;
1350
                        }
1351
                        //** end of redundant code **
1352
1353
                        $html .= ' style="' . $this->assembleCSS($cssClass) . '"';
0 ignored issues
show
Bug introduced by
It seems like $cssClass defined by 'column' . $colNum on line 1192 can also be of type string; however, PhpSpreadsheet\Writer\HTML::assembleCSS() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1354
                    }
1355
                    if ($colSpan > 1) {
1356
                        $html .= ' colspan="' . $colSpan . '"';
1357
                    }
1358
                    if ($rowSpan > 1) {
1359
                        $html .= ' rowspan="' . $rowSpan . '"';
1360
                    }
1361
                    $html .= '>';
1362
1363
                    // Image?
1364
                    $html .= $this->writeImageInCell($pSheet, $coordinate);
1365
1366
                    // Chart?
1367
                    if ($this->includeCharts) {
1368
                        $html .= $this->writeChartInCell($pSheet, $coordinate);
1369
                    }
1370
1371
                    // Cell data
1372
                    $html .= $cellData;
1373
1374
                    // Column end
1375
                    $html .= '</' . $cellType . '>' . PHP_EOL;
1376
                }
1377
1378
                // Next column
1379
                ++$colNum;
1380
            }
1381
1382
            // Write row end
1383
            $html .= '          </tr>' . PHP_EOL;
1384
1385
            // Return
1386
            return $html;
1387
        } else {
1388
            throw new \Spreadsheet\Writer\Exception('Invalid parameters passed.');
1389
        }
1390
    }
1391
1392
    /**
1393
     * Takes array where of CSS properties / values and converts to CSS string
1394
     *
1395
     * @param array
1396
     * @return string
1397
     */
1398
    private function assembleCSS($pValue = [])
1399
    {
1400
        $pairs = [];
1401
        foreach ($pValue as $property => $value) {
1402
            $pairs[] = $property . ':' . $value;
1403
        }
1404
        $string = implode('; ', $pairs);
1405
1406
        return $string;
1407
    }
1408
1409
    /**
1410
     * Get images root
1411
     *
1412
     * @return string
1413
     */
1414
    public function getImagesRoot()
1415
    {
1416
        return $this->imagesRoot;
1417
    }
1418
1419
    /**
1420
     * Set images root
1421
     *
1422
     * @param string $pValue
1423
     * @return Spreadsheet_Writer_HTML
1424
     */
1425
    public function setImagesRoot($pValue = '.')
1426
    {
1427
        $this->imagesRoot = $pValue;
1428
1429
        return $this;
1430
    }
1431
1432
    /**
1433
     * Get embed images
1434
     *
1435
     * @return bool
1436
     */
1437
    public function getEmbedImages()
1438
    {
1439
        return $this->embedImages;
1440
    }
1441
1442
    /**
1443
     * Set embed images
1444
     *
1445
     * @param bool $pValue
1446
     * @return Spreadsheet_Writer_HTML
1447
     */
1448
    public function setEmbedImages($pValue = '.')
1449
    {
1450
        $this->embedImages = $pValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pValue can also be of type string. However, the property $embedImages is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1451
1452
        return $this;
1453
    }
1454
1455
    /**
1456
     * Get use inline CSS?
1457
     *
1458
     * @return bool
1459
     */
1460
    public function getUseInlineCss()
1461
    {
1462
        return $this->useInlineCss;
1463
    }
1464
1465
    /**
1466
     * Set use inline CSS?
1467
     *
1468
     * @param bool $pValue
1469
     * @return Spreadsheet_Writer_HTML
1470
     */
1471
    public function setUseInlineCss($pValue = false)
1472
    {
1473
        $this->useInlineCss = $pValue;
1474
1475
        return $this;
1476
    }
1477
1478
    /**
1479
     * Add color to formatted string as inline style
1480
     *
1481
     * @param string $pValue Plain formatted value without color
1482
     * @param string $pFormat Format code
1483
     * @return string
1484
     */
1485
    public function formatColor($pValue, $pFormat)
1486
    {
1487
        // Color information, e.g. [Red] is always at the beginning
1488
        $color = null; // initialize
1489
        $matches = [];
1490
1491
        $color_regex = '/^\\[[a-zA-Z]+\\]/';
1492
        if (preg_match($color_regex, $pFormat, $matches)) {
1493
            $color = str_replace('[', '', $matches[0]);
1494
            $color = str_replace(']', '', $color);
1495
            $color = strtolower($color);
1496
        }
1497
1498
        // convert to PCDATA
1499
        $value = htmlspecialchars($pValue);
1500
1501
        // color span tag
1502
        if ($color !== null) {
1503
            $value = '<span style="color:' . $color . '">' . $value . '</span>';
1504
        }
1505
1506
        return $value;
1507
    }
1508
1509
    /**
1510
     * Calculate information about HTML colspan and rowspan which is not always the same as Excel's
1511
     */
1512
    private function calculateSpans()
1513
    {
1514
        // Identify all cells that should be omitted in HTML due to cell merge.
1515
        // In HTML only the upper-left cell should be written and it should have
1516
        //   appropriate rowspan / colspan attribute
1517
        $sheetIndexes = $this->sheetIndex !== null ?
1518
            [$this->sheetIndex] : range(0, $this->spreadsheet->getSheetCount() - 1);
1519
1520
        foreach ($sheetIndexes as $sheetIndex) {
1521
            $sheet = $this->spreadsheet->getSheet($sheetIndex);
1522
1523
            $candidateSpannedRow = [];
1524
1525
            // loop through all Excel merged cells
1526
            foreach ($sheet->getMergeCells() as $cells) {
1527
                list($cells) = \Spreadsheet\Cell::splitRange($cells);
1528
                $first = $cells[0];
1529
                $last = $cells[1];
1530
1531
                list($fc, $fr) = \Spreadsheet\Cell::coordinateFromString($first);
1532
                $fc = \Spreadsheet\Cell::columnIndexFromString($fc) - 1;
1533
1534
                list($lc, $lr) = \Spreadsheet\Cell::coordinateFromString($last);
1535
                $lc = \Spreadsheet\Cell::columnIndexFromString($lc) - 1;
1536
1537
                // loop through the individual cells in the individual merge
1538
                $r = $fr - 1;
1539
                while ($r++ < $lr) {
1540
                    // also, flag this row as a HTML row that is candidate to be omitted
1541
                    $candidateSpannedRow[$r] = $r;
1542
1543
                    $c = $fc - 1;
1544
                    while ($c++ < $lc) {
1545
                        if (!($c == $fc && $r == $fr)) {
1546
                            // not the upper-left cell (should not be written in HTML)
1547
                            $this->isSpannedCell[$sheetIndex][$r][$c] = [
1548
                                'baseCell' => [$fr, $fc],
1549
                            ];
1550
                        } else {
1551
                            // upper-left is the base cell that should hold the colspan/rowspan attribute
1552
                            $this->isBaseCell[$sheetIndex][$r][$c] = [
1553
                                'xlrowspan' => $lr - $fr + 1, // Excel rowspan
1554
                                'rowspan' => $lr - $fr + 1, // HTML rowspan, value may change
1555
                                'xlcolspan' => $lc - $fc + 1, // Excel colspan
1556
                                'colspan' => $lc - $fc + 1, // HTML colspan, value may change
1557
                            ];
1558
                        }
1559
                    }
1560
                }
1561
            }
1562
1563
            // Identify which rows should be omitted in HTML. These are the rows where all the cells
1564
            //   participate in a merge and the where base cells are somewhere above.
1565
            $countColumns = \Spreadsheet\Cell::columnIndexFromString($sheet->getHighestColumn());
1566
            foreach ($candidateSpannedRow as $rowIndex) {
1567
                if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) {
1568
                    if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
1569
                        $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
1570
                    };
1571
                }
1572
            }
1573
1574
            // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
1575
            if (isset($this->isSpannedRow[$sheetIndex])) {
1576
                foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) {
1577
                    $adjustedBaseCells = [];
1578
                    $c = -1;
1579
                    $e = $countColumns - 1;
1580
                    while ($c++ < $e) {
1581
                        $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
1582
1583
                        if (!in_array($baseCell, $adjustedBaseCells)) {
1584
                            // subtract rowspan by 1
1585
                            --$this->isBaseCell[$sheetIndex][ $baseCell[0] ][ $baseCell[1] ]['rowspan'];
1586
                            $adjustedBaseCells[] = $baseCell;
1587
                        }
1588
                    }
1589
                }
1590
            }
1591
1592
            // TODO: Same for columns
1593
        }
1594
1595
        // We have calculated the spans
1596
        $this->spansAreCalculated = true;
1597
    }
1598
1599
    private function setMargins(\Spreadsheet\Worksheet $pSheet)
1600
    {
1601
        $htmlPage = '@page { ';
1602
        $htmlBody = 'body { ';
1603
1604
        $left = StringHelper::formatNumber($pSheet->getPageMargins()->getLeft()) . 'in; ';
1605
        $htmlPage .= 'margin-left: ' . $left;
1606
        $htmlBody .= 'margin-left: ' . $left;
1607
        $right = StringHelper::formatNumber($pSheet->getPageMargins()->getRight()) . 'in; ';
1608
        $htmlPage .= 'margin-right: ' . $right;
1609
        $htmlBody .= 'margin-right: ' . $right;
1610
        $top = StringHelper::formatNumber($pSheet->getPageMargins()->getTop()) . 'in; ';
1611
        $htmlPage .= 'margin-top: ' . $top;
1612
        $htmlBody .= 'margin-top: ' . $top;
1613
        $bottom = StringHelper::formatNumber($pSheet->getPageMargins()->getBottom()) . 'in; ';
1614
        $htmlPage .= 'margin-bottom: ' . $bottom;
1615
        $htmlBody .= 'margin-bottom: ' . $bottom;
1616
1617
        $htmlPage .= "}\n";
1618
        $htmlBody .= "}\n";
1619
1620
        return "<style>\n" . $htmlPage . $htmlBody . "</style>\n";
1621
    }
1622
}
1623