Completed
Push — develop ( 44e246...6e4e0a )
by Adrien
19:07
created

Xls::canRead()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0932

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 3
nop 1
dl 0
loc 16
ccs 5
cts 7
cp 0.7143
crap 2.0932
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use PhpOffice\PhpSpreadsheet\Cell;
6
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
7
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
8
use PhpOffice\PhpSpreadsheet\NamedRange;
9
use PhpOffice\PhpSpreadsheet\RichText;
10
use PhpOffice\PhpSpreadsheet\Shared\CodePage;
11
use PhpOffice\PhpSpreadsheet\Shared\Date;
12
use PhpOffice\PhpSpreadsheet\Shared\Escher;
13
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
14
use PhpOffice\PhpSpreadsheet\Shared\File;
15
use PhpOffice\PhpSpreadsheet\Shared\OLE;
16
use PhpOffice\PhpSpreadsheet\Shared\OLERead;
17
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
18
use PhpOffice\PhpSpreadsheet\Spreadsheet;
19
use PhpOffice\PhpSpreadsheet\Style;
20
use PhpOffice\PhpSpreadsheet\Style\Alignment;
21
use PhpOffice\PhpSpreadsheet\Style\Borders;
22
use PhpOffice\PhpSpreadsheet\Style\Font;
23
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
24
use PhpOffice\PhpSpreadsheet\Style\Protection;
25
use PhpOffice\PhpSpreadsheet\Worksheet;
26
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
27
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
28
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
29
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
30
31
/**
32
 * Copyright (c) 2006 - 2016 PhpSpreadsheet.
33
 *
34
 * This library is free software; you can redistribute it and/or
35
 * modify it under the terms of the GNU Lesser General Public
36
 * License as published by the Free Software Foundation; either
37
 * version 2.1 of the License, or (at your option) any later version.
38
 *
39
 * This library is distributed in the hope that it will be useful,
40
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
41
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
42
 * Lesser General Public License for more details.
43
 *
44
 * You should have received a copy of the GNU Lesser General Public
45
 * License along with this library; if not, write to the Free Software
46
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
47
 *
48
 * @category   PhpSpreadsheet
49
 *
50
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
51
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
52
 */
53
54
// Original file header of ParseXL (used as the base for this class):
55
// --------------------------------------------------------------------------------
56
// Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
57
// trex005, and mmp11 (SourceForge.net)
58
// http://sourceforge.net/projects/phpexcelreader/
59
// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
60
//     Modelled moreso after Perl Excel Parse/Write modules
61
//     Added Parse_Excel_Spreadsheet object
62
//         Reads a whole worksheet or tab as row,column array or as
63
//         associated hash of indexed rows and named column fields
64
//     Added variables for worksheet (tab) indexes and names
65
//     Added an object call for loading individual woorksheets
66
//     Changed default indexing defaults to 0 based arrays
67
//     Fixed date/time and percent formats
68
//     Includes patches found at SourceForge...
69
//         unicode patch by nobody
70
//         unpack("d") machine depedency patch by matchy
71
//         boundsheet utf16 patch by bjaenichen
72
//     Renamed functions for shorter names
73
//     General code cleanup and rigor, including <80 column width
74
//     Included a testcase Excel file and PHP example calls
75
//     Code works for PHP 5.x
76
77
// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
78
// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
79
//     Decoding of formula conditions, results, and tokens.
80
//     Support for user-defined named cells added as an array "namedcells"
81
//         Patch code for user-defined named cells supports single cells only.
82
//         NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
83
//         external sheet reference structure
84
class Xls extends BaseReader implements IReader
85
{
86
    // ParseXL definitions
87
    const XLS_BIFF8 = 0x0600;
88
    const XLS_BIFF7 = 0x0500;
89
    const XLS_WORKBOOKGLOBALS = 0x0005;
90
    const XLS_WORKSHEET = 0x0010;
91
92
    // record identifiers
93
    const XLS_TYPE_FORMULA = 0x0006;
94
    const XLS_TYPE_EOF = 0x000a;
95
    const XLS_TYPE_PROTECT = 0x0012;
96
    const XLS_TYPE_OBJECTPROTECT = 0x0063;
97
    const XLS_TYPE_SCENPROTECT = 0x00dd;
98
    const XLS_TYPE_PASSWORD = 0x0013;
99
    const XLS_TYPE_HEADER = 0x0014;
100
    const XLS_TYPE_FOOTER = 0x0015;
101
    const XLS_TYPE_EXTERNSHEET = 0x0017;
102
    const XLS_TYPE_DEFINEDNAME = 0x0018;
103
    const XLS_TYPE_VERTICALPAGEBREAKS = 0x001a;
104
    const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001b;
105
    const XLS_TYPE_NOTE = 0x001c;
106
    const XLS_TYPE_SELECTION = 0x001d;
107
    const XLS_TYPE_DATEMODE = 0x0022;
108
    const XLS_TYPE_EXTERNNAME = 0x0023;
109
    const XLS_TYPE_LEFTMARGIN = 0x0026;
110
    const XLS_TYPE_RIGHTMARGIN = 0x0027;
111
    const XLS_TYPE_TOPMARGIN = 0x0028;
112
    const XLS_TYPE_BOTTOMMARGIN = 0x0029;
113
    const XLS_TYPE_PRINTGRIDLINES = 0x002b;
114
    const XLS_TYPE_FILEPASS = 0x002f;
115
    const XLS_TYPE_FONT = 0x0031;
116
    const XLS_TYPE_CONTINUE = 0x003c;
117
    const XLS_TYPE_PANE = 0x0041;
118
    const XLS_TYPE_CODEPAGE = 0x0042;
119
    const XLS_TYPE_DEFCOLWIDTH = 0x0055;
120
    const XLS_TYPE_OBJ = 0x005d;
121
    const XLS_TYPE_COLINFO = 0x007d;
122
    const XLS_TYPE_IMDATA = 0x007f;
123
    const XLS_TYPE_SHEETPR = 0x0081;
124
    const XLS_TYPE_HCENTER = 0x0083;
125
    const XLS_TYPE_VCENTER = 0x0084;
126
    const XLS_TYPE_SHEET = 0x0085;
127
    const XLS_TYPE_PALETTE = 0x0092;
128
    const XLS_TYPE_SCL = 0x00a0;
129
    const XLS_TYPE_PAGESETUP = 0x00a1;
130
    const XLS_TYPE_MULRK = 0x00bd;
131
    const XLS_TYPE_MULBLANK = 0x00be;
132
    const XLS_TYPE_DBCELL = 0x00d7;
133
    const XLS_TYPE_XF = 0x00e0;
134
    const XLS_TYPE_MERGEDCELLS = 0x00e5;
135
    const XLS_TYPE_MSODRAWINGGROUP = 0x00eb;
136
    const XLS_TYPE_MSODRAWING = 0x00ec;
137
    const XLS_TYPE_SST = 0x00fc;
138
    const XLS_TYPE_LABELSST = 0x00fd;
139
    const XLS_TYPE_EXTSST = 0x00ff;
140
    const XLS_TYPE_EXTERNALBOOK = 0x01ae;
141
    const XLS_TYPE_DATAVALIDATIONS = 0x01b2;
142
    const XLS_TYPE_TXO = 0x01b6;
143
    const XLS_TYPE_HYPERLINK = 0x01b8;
144
    const XLS_TYPE_DATAVALIDATION = 0x01be;
145
    const XLS_TYPE_DIMENSION = 0x0200;
146
    const XLS_TYPE_BLANK = 0x0201;
147
    const XLS_TYPE_NUMBER = 0x0203;
148
    const XLS_TYPE_LABEL = 0x0204;
149
    const XLS_TYPE_BOOLERR = 0x0205;
150
    const XLS_TYPE_STRING = 0x0207;
151
    const XLS_TYPE_ROW = 0x0208;
152
    const XLS_TYPE_INDEX = 0x020b;
153
    const XLS_TYPE_ARRAY = 0x0221;
154
    const XLS_TYPE_DEFAULTROWHEIGHT = 0x0225;
155
    const XLS_TYPE_WINDOW2 = 0x023e;
156
    const XLS_TYPE_RK = 0x027e;
157
    const XLS_TYPE_STYLE = 0x0293;
158
    const XLS_TYPE_FORMAT = 0x041e;
159
    const XLS_TYPE_SHAREDFMLA = 0x04bc;
160
    const XLS_TYPE_BOF = 0x0809;
161
    const XLS_TYPE_SHEETPROTECTION = 0x0867;
162
    const XLS_TYPE_RANGEPROTECTION = 0x0868;
163
    const XLS_TYPE_SHEETLAYOUT = 0x0862;
164
    const XLS_TYPE_XFEXT = 0x087d;
165
    const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
166
    const XLS_TYPE_UNKNOWN = 0xffff;
167
168
    // Encryption type
169
    const MS_BIFF_CRYPTO_NONE = 0;
170
    const MS_BIFF_CRYPTO_XOR = 1;
171
    const MS_BIFF_CRYPTO_RC4 = 2;
172
173
    // Size of stream blocks when using RC4 encryption
174
    const REKEY_BLOCK = 0x400;
175
176
    /**
177
     * Summary Information stream data.
178
     *
179
     * @var string
180
     */
181
    private $summaryInformation;
182
183
    /**
184
     * Extended Summary Information stream data.
185
     *
186
     * @var string
187
     */
188
    private $documentSummaryInformation;
189
190
    /**
191
     * User-Defined Properties stream data.
192
     *
193
     * @var string
194
     */
195
    private $userDefinedProperties;
0 ignored issues
show
Unused Code introduced by
The property $userDefinedProperties is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
196
197
    /**
198
     * Workbook stream data. (Includes workbook globals substream as well as sheet substreams).
199
     *
200
     * @var string
201
     */
202
    private $data;
203
204
    /**
205
     * Size in bytes of $this->data.
206
     *
207
     * @var int
208
     */
209
    private $dataSize;
210
211
    /**
212
     * Current position in stream.
213
     *
214
     * @var int
215
     */
216
    private $pos;
217
218
    /**
219
     * Workbook to be returned by the reader.
220
     *
221
     * @var Spreadsheet
222
     */
223
    private $spreadsheet;
224
225
    /**
226
     * Worksheet that is currently being built by the reader.
227
     *
228
     * @var Worksheet
229
     */
230
    private $phpSheet;
231
232
    /**
233
     * BIFF version.
234
     *
235
     * @var int
236
     */
237
    private $version;
238
239
    /**
240
     * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
241
     * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
242
     *
243
     * @var string
244
     */
245
    private $codepage;
246
247
    /**
248
     * Shared formats.
249
     *
250
     * @var array
251
     */
252
    private $formats;
253
254
    /**
255
     * Shared fonts.
256
     *
257
     * @var array
258
     */
259
    private $objFonts;
260
261
    /**
262
     * Color palette.
263
     *
264
     * @var array
265
     */
266
    private $palette;
267
268
    /**
269
     * Worksheets.
270
     *
271
     * @var array
272
     */
273
    private $sheets;
274
275
    /**
276
     * External books.
277
     *
278
     * @var array
279
     */
280
    private $externalBooks;
281
282
    /**
283
     * REF structures. Only applies to BIFF8.
284
     *
285
     * @var array
286
     */
287
    private $ref;
288
289
    /**
290
     * External names.
291
     *
292
     * @var array
293
     */
294
    private $externalNames;
295
296
    /**
297
     * Defined names.
298
     *
299
     * @var array
300
     */
301
    private $definedname;
302
303
    /**
304
     * Shared strings. Only applies to BIFF8.
305
     *
306
     * @var array
307
     */
308
    private $sst;
309
310
    /**
311
     * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
312
     *
313
     * @var bool
314
     */
315
    private $frozen;
316
317
    /**
318
     * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
319
     *
320
     * @var bool
321
     */
322
    private $isFitToPages;
323
324
    /**
325
     * Objects. One OBJ record contributes with one entry.
326
     *
327
     * @var array
328
     */
329
    private $objs;
330
331
    /**
332
     * Text Objects. One TXO record corresponds with one entry.
333
     *
334
     * @var array
335
     */
336
    private $textObjects;
337
338
    /**
339
     * Cell Annotations (BIFF8).
340
     *
341
     * @var array
342
     */
343
    private $cellNotes;
344
345
    /**
346
     * The combined MSODRAWINGGROUP data.
347
     *
348
     * @var string
349
     */
350
    private $drawingGroupData;
351
352
    /**
353
     * The combined MSODRAWING data (per sheet).
354
     *
355
     * @var string
356
     */
357
    private $drawingData;
358
359
    /**
360
     * Keep track of XF index.
361
     *
362
     * @var int
363
     */
364
    private $xfIndex;
365
366
    /**
367
     * Mapping of XF index (that is a cell XF) to final index in cellXf collection.
368
     *
369
     * @var array
370
     */
371
    private $mapCellXfIndex;
372
373
    /**
374
     * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection.
375
     *
376
     * @var array
377
     */
378
    private $mapCellStyleXfIndex;
379
380
    /**
381
     * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
382
     *
383
     * @var array
384
     */
385
    private $sharedFormulas;
386
387
    /**
388
     * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
389
     * refers to a shared formula.
390
     *
391
     * @var array
392
     */
393
    private $sharedFormulaParts;
394
395
    /**
396
     * The type of encryption in use.
397
     *
398
     * @var int
399
     */
400
    private $encryption = 0;
401
402
    /**
403
     * The position in the stream after which contents are encrypted.
404
     *
405
     * @var int
406
     */
407
    private $encryptionStartPos = false;
408
409
    /**
410
     * The current RC4 decryption object.
411
     *
412
     * @var Xls\RC4
413
     */
414
    private $rc4Key = null;
415
416
    /**
417
     * The position in the stream that the RC4 decryption object was left at.
418
     *
419
     * @var int
420
     */
421
    private $rc4Pos = 0;
422
423
    /**
424
     * The current MD5 context state.
425
     *
426
     * @var string
427
     */
428
    private $md5Ctxt = null;
429
430
    /**
431
     * Create a new Xls Reader instance.
432
     */
433 5
    public function __construct()
434
    {
435 5
        $this->readFilter = new DefaultReadFilter();
436 5
    }
437
438
    /**
439
     * Can the current IReader read the file?
440
     *
441
     * @param string $pFilename
442
     *
443
     * @throws Exception
444
     *
445
     * @return bool
446
     */
447 3
    public function canRead($pFilename)
448
    {
449 3
        File::assertFile($pFilename);
450
451
        try {
452
            // Use ParseXL for the hard work.
453 3
            $ole = new OLERead();
454
455
            // get excel data
456 3
            $ole->read($pFilename);
457
458 3
            return true;
459
        } catch (PhpSpreadsheetException $e) {
460
            return false;
461
        }
462
    }
463
464
    /**
465
     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
466
     *
467
     * @param string $pFilename
468
     *
469
     * @throws Exception
470
     */
471
    public function listWorksheetNames($pFilename)
472
    {
473
        File::assertFile($pFilename);
474
475
        $worksheetNames = [];
476
477
        // Read the OLE file
478
        $this->loadOLE($pFilename);
479
480
        // total byte size of Excel data (workbook global substream + sheet substreams)
481
        $this->dataSize = strlen($this->data);
482
483
        $this->pos = 0;
484
        $this->sheets = [];
485
486
        // Parse Workbook Global Substream
487 View Code Duplication
        while ($this->pos < $this->dataSize) {
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...
488
            $code = self::getInt2d($this->data, $this->pos);
489
490
            switch ($code) {
491
                case self::XLS_TYPE_BOF:
492
                    $this->readBof();
493
                    break;
494
                case self::XLS_TYPE_SHEET:
495
                    $this->readSheet();
496
                    break;
497
                case self::XLS_TYPE_EOF:
498
                    $this->readDefault();
499
                    break 2;
500
                default:
501
                    $this->readDefault();
502
                    break;
503
            }
504
        }
505
506
        foreach ($this->sheets as $sheet) {
507
            if ($sheet['sheetType'] != 0x00) {
508
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
509
                continue;
510
            }
511
512
            $worksheetNames[] = $sheet['name'];
513
        }
514
515
        return $worksheetNames;
516
    }
517
518
    /**
519
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
520
     *
521
     * @param string $pFilename
522
     *
523
     * @throws Exception
524
     */
525
    public function listWorksheetInfo($pFilename)
526
    {
527
        File::assertFile($pFilename);
528
529
        $worksheetInfo = [];
530
531
        // Read the OLE file
532
        $this->loadOLE($pFilename);
533
534
        // total byte size of Excel data (workbook global substream + sheet substreams)
535
        $this->dataSize = strlen($this->data);
536
537
        // initialize
538
        $this->pos = 0;
539
        $this->sheets = [];
540
541
        // Parse Workbook Global Substream
542 View Code Duplication
        while ($this->pos < $this->dataSize) {
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...
543
            $code = self::getInt2d($this->data, $this->pos);
544
545
            switch ($code) {
546
                case self::XLS_TYPE_BOF:
547
                    $this->readBof();
548
                    break;
549
                case self::XLS_TYPE_SHEET:
550
                    $this->readSheet();
551
                    break;
552
                case self::XLS_TYPE_EOF:
553
                    $this->readDefault();
554
                    break 2;
555
                default:
556
                    $this->readDefault();
557
                    break;
558
            }
559
        }
560
561
        // Parse the individual sheets
562
        foreach ($this->sheets as $sheet) {
563
            if ($sheet['sheetType'] != 0x00) {
564
                // 0x00: Worksheet
565
                // 0x02: Chart
566
                // 0x06: Visual Basic module
567
                continue;
568
            }
569
570
            $tmpInfo = [];
571
            $tmpInfo['worksheetName'] = $sheet['name'];
572
            $tmpInfo['lastColumnLetter'] = 'A';
573
            $tmpInfo['lastColumnIndex'] = 0;
574
            $tmpInfo['totalRows'] = 0;
575
            $tmpInfo['totalColumns'] = 0;
576
577
            $this->pos = $sheet['offset'];
578
579
            while ($this->pos <= $this->dataSize - 4) {
580
                $code = self::getInt2d($this->data, $this->pos);
581
582
                switch ($code) {
583
                    case self::XLS_TYPE_RK:
584
                    case self::XLS_TYPE_LABELSST:
585
                    case self::XLS_TYPE_NUMBER:
586
                    case self::XLS_TYPE_FORMULA:
587
                    case self::XLS_TYPE_BOOLERR:
588
                    case self::XLS_TYPE_LABEL:
589
                        $length = self::getInt2d($this->data, $this->pos + 2);
590
                        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
591
592
                        // move stream pointer to next record
593
                        $this->pos += 4 + $length;
594
595
                        $rowIndex = self::getInt2d($recordData, 0) + 1;
596
                        $columnIndex = self::getInt2d($recordData, 2);
597
598
                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
599
                        $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
600
                        break;
601
                    case self::XLS_TYPE_BOF:
602
                        $this->readBof();
603
                        break;
604
                    case self::XLS_TYPE_EOF:
605
                        $this->readDefault();
606
                        break 2;
607
                    default:
608
                        $this->readDefault();
609
                        break;
610
                }
611
            }
612
613
            $tmpInfo['lastColumnLetter'] = Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
614
            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
615
616
            $worksheetInfo[] = $tmpInfo;
617
        }
618
619
        return $worksheetInfo;
620
    }
621
622
    /**
623
     * Loads PhpSpreadsheet from file.
624
     *
625
     * @param string $pFilename
626
     *
627
     * @throws Exception
628
     *
629
     * @return Spreadsheet
630
     */
631 4
    public function load($pFilename)
632
    {
633
        // Read the OLE file
634 4
        $this->loadOLE($pFilename);
635
636
        // Initialisations
637 4
        $this->spreadsheet = new Spreadsheet();
638 4
        $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
639 4
        if (!$this->readDataOnly) {
640 4
            $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
641 4
            $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
642
        }
643
644
        // Read the summary information stream (containing meta data)
645 4
        $this->readSummaryInformation();
646
647
        // Read the Additional document summary information stream (containing application-specific meta data)
648 4
        $this->readDocumentSummaryInformation();
649
650
        // total byte size of Excel data (workbook global substream + sheet substreams)
651 4
        $this->dataSize = strlen($this->data);
652
653
        // initialize
654 4
        $this->pos = 0;
655 4
        $this->codepage = 'CP1252';
656 4
        $this->formats = [];
657 4
        $this->objFonts = [];
658 4
        $this->palette = [];
659 4
        $this->sheets = [];
660 4
        $this->externalBooks = [];
661 4
        $this->ref = [];
662 4
        $this->definedname = [];
663 4
        $this->sst = [];
664 4
        $this->drawingGroupData = '';
665 4
        $this->xfIndex = '';
0 ignored issues
show
Documentation Bug introduced by
The property $xfIndex was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
666 4
        $this->mapCellXfIndex = [];
667 4
        $this->mapCellStyleXfIndex = [];
668
669
        // Parse Workbook Global Substream
670 4
        while ($this->pos < $this->dataSize) {
671 4
            $code = self::getInt2d($this->data, $this->pos);
672
673
            switch ($code) {
674 4
                case self::XLS_TYPE_BOF:
675 4
                    $this->readBof();
676 4
                    break;
677 4
                case self::XLS_TYPE_FILEPASS:
678
                    $this->readFilepass();
679
                    break;
680 4
                case self::XLS_TYPE_CODEPAGE:
681 4
                    $this->readCodepage();
682 4
                    break;
683 4
                case self::XLS_TYPE_DATEMODE:
684 4
                    $this->readDateMode();
685 4
                    break;
686 4
                case self::XLS_TYPE_FONT:
687 4
                    $this->readFont();
688 4
                    break;
689 4
                case self::XLS_TYPE_FORMAT:
690 4
                    $this->readFormat();
691 4
                    break;
692 4
                case self::XLS_TYPE_XF:
693 4
                    $this->readXf();
694 4
                    break;
695 4
                case self::XLS_TYPE_XFEXT:
696 2
                    $this->readXfExt();
697 2
                    break;
698 4
                case self::XLS_TYPE_STYLE:
699 4
                    $this->readStyle();
700 4
                    break;
701 4
                case self::XLS_TYPE_PALETTE:
702 3
                    $this->readPalette();
703 3
                    break;
704 4
                case self::XLS_TYPE_SHEET:
705 4
                    $this->readSheet();
706 4
                    break;
707 4
                case self::XLS_TYPE_EXTERNALBOOK:
708 3
                    $this->readExternalBook();
709 3
                    break;
710 4
                case self::XLS_TYPE_EXTERNNAME:
711
                    $this->readExternName();
712
                    break;
713 4
                case self::XLS_TYPE_EXTERNSHEET:
714 3
                    $this->readExternSheet();
715 3
                    break;
716 4
                case self::XLS_TYPE_DEFINEDNAME:
717 1
                    $this->readDefinedName();
718 1
                    break;
719 4
                case self::XLS_TYPE_MSODRAWINGGROUP:
720 3
                    $this->readMsoDrawingGroup();
721 3
                    break;
722 4
                case self::XLS_TYPE_SST:
723 4
                    $this->readSst();
724 4
                    break;
725 4
                case self::XLS_TYPE_EOF:
726 4
                    $this->readDefault();
727 4
                    break 2;
728
                default:
729 4
                    $this->readDefault();
730 4
                    break;
731
            }
732
        }
733
734
        // Resolve indexed colors for font, fill, and border colors
735
        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
736 4
        if (!$this->readDataOnly) {
737 4
            foreach ($this->objFonts as $objFont) {
738 4 View Code Duplication
                if (isset($objFont->colorIndex)) {
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...
739 4
                    $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
740 4
                    $objFont->getColor()->setRGB($color['rgb']);
741
                }
742
            }
743
744 4
            foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
745
                // fill start and end color
746 4
                $fill = $objStyle->getFill();
747
748 4
                if (isset($fill->startcolorIndex)) {
749 4
                    $startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
750 4
                    $fill->getStartColor()->setRGB($startColor['rgb']);
751
                }
752 4
                if (isset($fill->endcolorIndex)) {
753 4
                    $endColor = Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version);
0 ignored issues
show
Bug introduced by
The property endcolorIndex does not seem to exist. Did you mean endColor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
754 4
                    $fill->getEndColor()->setRGB($endColor['rgb']);
755
                }
756
757
                // border colors
758 4
                $top = $objStyle->getBorders()->getTop();
759 4
                $right = $objStyle->getBorders()->getRight();
760 4
                $bottom = $objStyle->getBorders()->getBottom();
761 4
                $left = $objStyle->getBorders()->getLeft();
762 4
                $diagonal = $objStyle->getBorders()->getDiagonal();
763
764 4 View Code Duplication
                if (isset($top->colorIndex)) {
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...
765 4
                    $borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version);
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpOffice\PhpSpreadsheet\Style\Border.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
766 4
                    $top->getColor()->setRGB($borderTopColor['rgb']);
767
                }
768 4 View Code Duplication
                if (isset($right->colorIndex)) {
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...
769 4
                    $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
770 4
                    $right->getColor()->setRGB($borderRightColor['rgb']);
771
                }
772 4 View Code Duplication
                if (isset($bottom->colorIndex)) {
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...
773 4
                    $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
774 4
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
775
                }
776 4 View Code Duplication
                if (isset($left->colorIndex)) {
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...
777 4
                    $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
778 4
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
779
                }
780 4 View Code Duplication
                if (isset($diagonal->colorIndex)) {
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...
781 4
                    $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
782 4
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
783
                }
784
            }
785
        }
786
787
        // treat MSODRAWINGGROUP records, workbook-level Escher
788 4
        if (!$this->readDataOnly && $this->drawingGroupData) {
789 3
            $escherWorkbook = new Escher();
790 3
            $reader = new Xls\Escher($escherWorkbook);
791 3
            $escherWorkbook = $reader->load($this->drawingGroupData);
792
        }
793
794
        // Parse the individual sheets
795 4
        foreach ($this->sheets as $sheet) {
796 4
            if ($sheet['sheetType'] != 0x00) {
797
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
798
                continue;
799
            }
800
801
            // check if sheet should be skipped
802 4
            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
803
                continue;
804
            }
805
806
            // add sheet to PhpSpreadsheet object
807 4
            $this->phpSheet = $this->spreadsheet->createSheet();
808
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
809
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
810
            //        name in line with the formula, not the reverse
811 4
            $this->phpSheet->setTitle($sheet['name'], false);
812 4
            $this->phpSheet->setSheetState($sheet['sheetState']);
813
814 4
            $this->pos = $sheet['offset'];
815
816
            // Initialize isFitToPages. May change after reading SHEETPR record.
817 4
            $this->isFitToPages = false;
818
819
            // Initialize drawingData
820 4
            $this->drawingData = '';
821
822
            // Initialize objs
823 4
            $this->objs = [];
824
825
            // Initialize shared formula parts
826 4
            $this->sharedFormulaParts = [];
827
828
            // Initialize shared formulas
829 4
            $this->sharedFormulas = [];
830
831
            // Initialize text objs
832 4
            $this->textObjects = [];
833
834
            // Initialize cell annotations
835 4
            $this->cellNotes = [];
836 4
            $this->textObjRef = -1;
0 ignored issues
show
Bug introduced by
The property textObjRef does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
837
838 4
            while ($this->pos <= $this->dataSize - 4) {
839 4
                $code = self::getInt2d($this->data, $this->pos);
840
841
                switch ($code) {
842 4
                    case self::XLS_TYPE_BOF:
843 4
                        $this->readBof();
844 4
                        break;
845 4
                    case self::XLS_TYPE_PRINTGRIDLINES:
846 4
                        $this->readPrintGridlines();
847 4
                        break;
848 4
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
849 3
                        $this->readDefaultRowHeight();
850 3
                        break;
851 4
                    case self::XLS_TYPE_SHEETPR:
852 4
                        $this->readSheetPr();
853 4
                        break;
854 4
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
855
                        $this->readHorizontalPageBreaks();
856
                        break;
857 4
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
858
                        $this->readVerticalPageBreaks();
859
                        break;
860 4
                    case self::XLS_TYPE_HEADER:
861 4
                        $this->readHeader();
862 4
                        break;
863 4
                    case self::XLS_TYPE_FOOTER:
864 4
                        $this->readFooter();
865 4
                        break;
866 4
                    case self::XLS_TYPE_HCENTER:
867 4
                        $this->readHcenter();
868 4
                        break;
869 4
                    case self::XLS_TYPE_VCENTER:
870 4
                        $this->readVcenter();
871 4
                        break;
872 4
                    case self::XLS_TYPE_LEFTMARGIN:
873 4
                        $this->readLeftMargin();
874 4
                        break;
875 4
                    case self::XLS_TYPE_RIGHTMARGIN:
876 4
                        $this->readRightMargin();
877 4
                        break;
878 4
                    case self::XLS_TYPE_TOPMARGIN:
879 4
                        $this->readTopMargin();
880 4
                        break;
881 4
                    case self::XLS_TYPE_BOTTOMMARGIN:
882 4
                        $this->readBottomMargin();
883 4
                        break;
884 4
                    case self::XLS_TYPE_PAGESETUP:
885 4
                        $this->readPageSetup();
886 4
                        break;
887 4
                    case self::XLS_TYPE_PROTECT:
888 1
                        $this->readProtect();
889 1
                        break;
890 4
                    case self::XLS_TYPE_SCENPROTECT:
891
                        $this->readScenProtect();
892
                        break;
893 4
                    case self::XLS_TYPE_OBJECTPROTECT:
894
                        $this->readObjectProtect();
895
                        break;
896 4
                    case self::XLS_TYPE_PASSWORD:
897
                        $this->readPassword();
898
                        break;
899 4
                    case self::XLS_TYPE_DEFCOLWIDTH:
900 4
                        $this->readDefColWidth();
901 4
                        break;
902 4
                    case self::XLS_TYPE_COLINFO:
903 4
                        $this->readColInfo();
904 4
                        break;
905 4
                    case self::XLS_TYPE_DIMENSION:
906 4
                        $this->readDefault();
907 4
                        break;
908 4
                    case self::XLS_TYPE_ROW:
909 3
                        $this->readRow();
910 3
                        break;
911 4
                    case self::XLS_TYPE_DBCELL:
912 2
                        $this->readDefault();
913 2
                        break;
914 4
                    case self::XLS_TYPE_RK:
915 1
                        $this->readRk();
916 1
                        break;
917 4
                    case self::XLS_TYPE_LABELSST:
918 4
                        $this->readLabelSst();
919 4
                        break;
920 4
                    case self::XLS_TYPE_MULRK:
921
                        $this->readMulRk();
922
                        break;
923 4
                    case self::XLS_TYPE_NUMBER:
924 1
                        $this->readNumber();
925 1
                        break;
926 4
                    case self::XLS_TYPE_FORMULA:
927 2
                        $this->readFormula();
928 2
                        break;
929 4
                    case self::XLS_TYPE_SHAREDFMLA:
930
                        $this->readSharedFmla();
931
                        break;
932 4
                    case self::XLS_TYPE_BOOLERR:
933
                        $this->readBoolErr();
934
                        break;
935 4
                    case self::XLS_TYPE_MULBLANK:
936 1
                        $this->readMulBlank();
937 1
                        break;
938 4
                    case self::XLS_TYPE_LABEL:
939
                        $this->readLabel();
940
                        break;
941 4
                    case self::XLS_TYPE_BLANK:
942 2
                        $this->readBlank();
943 2
                        break;
944 4
                    case self::XLS_TYPE_MSODRAWING:
945 3
                        $this->readMsoDrawing();
946 3
                        break;
947 4
                    case self::XLS_TYPE_OBJ:
948 3
                        $this->readObj();
949 3
                        break;
950 4
                    case self::XLS_TYPE_WINDOW2:
951 4
                        $this->readWindow2();
952 4
                        break;
953 4
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
954 3
                        $this->readPageLayoutView();
955 3
                        break;
956 4
                    case self::XLS_TYPE_SCL:
957
                        $this->readScl();
958
                        break;
959 4
                    case self::XLS_TYPE_PANE:
960
                        $this->readPane();
961
                        break;
962 4
                    case self::XLS_TYPE_SELECTION:
963 4
                        $this->readSelection();
964 4
                        break;
965 4
                    case self::XLS_TYPE_MERGEDCELLS:
966 2
                        $this->readMergedCells();
967 2
                        break;
968 4
                    case self::XLS_TYPE_HYPERLINK:
969 2
                        $this->readHyperLink();
970 2
                        break;
971 4
                    case self::XLS_TYPE_DATAVALIDATIONS:
972
                        $this->readDataValidations();
973
                        break;
974 4
                    case self::XLS_TYPE_DATAVALIDATION:
975
                        $this->readDataValidation();
976
                        break;
977 4
                    case self::XLS_TYPE_SHEETLAYOUT:
978 2
                        $this->readSheetLayout();
979 2
                        break;
980 4
                    case self::XLS_TYPE_SHEETPROTECTION:
981 4
                        $this->readSheetProtection();
982 4
                        break;
983 4
                    case self::XLS_TYPE_RANGEPROTECTION:
984 1
                        $this->readRangeProtection();
985 1
                        break;
986 4
                    case self::XLS_TYPE_NOTE:
987 1
                        $this->readNote();
988 1
                        break;
989
                    //case self::XLS_TYPE_IMDATA:                $this->readImData();                    break;
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% 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...
990 4
                    case self::XLS_TYPE_TXO:
991 1
                        $this->readTextObject();
992 1
                        break;
993 4
                    case self::XLS_TYPE_CONTINUE:
994
                        $this->readContinue();
995
                        break;
996 4
                    case self::XLS_TYPE_EOF:
997 4
                        $this->readDefault();
998 4
                        break 2;
999
                    default:
1000 4
                        $this->readDefault();
1001 4
                        break;
1002
                }
1003
            }
1004
1005
            // treat MSODRAWING records, sheet-level Escher
1006 4
            if (!$this->readDataOnly && $this->drawingData) {
1007 3
                $escherWorksheet = new Escher();
1008 3
                $reader = new Xls\Escher($escherWorksheet);
1009 3
                $escherWorksheet = $reader->load($this->drawingData);
1010
1011
                // get all spContainers in one long array, so they can be mapped to OBJ records
1012 3
                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
1013
            }
1014
1015
            // treat OBJ records
1016 4
            foreach ($this->objs as $n => $obj) {
1017
                // the first shape container never has a corresponding OBJ record, hence $n + 1
1018 3
                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
1019 3
                    $spContainer = $allSpContainers[$n + 1];
1020
1021
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
1022 3
                    if ($spContainer->getNestingLevel() > 1) {
1023
                        continue;
1024
                    }
1025
1026
                    // calculate the width and height of the shape
1027 3
                    list($startColumn, $startRow) = Cell::coordinateFromString($spContainer->getStartCoordinates());
1028 3
                    list($endColumn, $endRow) = Cell::coordinateFromString($spContainer->getEndCoordinates());
1029
1030 3
                    $startOffsetX = $spContainer->getStartOffsetX();
1031 3
                    $startOffsetY = $spContainer->getStartOffsetY();
1032 3
                    $endOffsetX = $spContainer->getEndOffsetX();
1033 3
                    $endOffsetY = $spContainer->getEndOffsetY();
1034
1035 3
                    $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
1036 3
                    $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
1037
1038
                    // calculate offsetX and offsetY of the shape
1039 3
                    $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024;
1040 3
                    $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256;
1041
1042 3
                    switch ($obj['otObjType']) {
1043 3
                        case 0x19:
1044
                            // Note
1045 1
                            if (isset($this->cellNotes[$obj['idObjID']])) {
1046 1
                                $cellNote = $this->cellNotes[$obj['idObjID']];
0 ignored issues
show
Unused Code introduced by
$cellNote 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...
1047
1048 1 View Code Duplication
                                if (isset($this->textObjects[$obj['idObjID']])) {
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...
1049 1
                                    $textObject = $this->textObjects[$obj['idObjID']];
1050 1
                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
1051
                                }
1052
                            }
1053 1
                            break;
1054 3
                        case 0x08:
1055
                            // picture
1056
                            // get index to BSE entry (1-based)
1057 3
                            $BSEindex = $spContainer->getOPT(0x0104);
1058
1059
                            // If there is no BSE Index, we will fail here and other fields are not read.
1060
                            // Fix by checking here.
1061
                            // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
1062
                            // More likely : a uncompatible picture
1063 3
                            if (!$BSEindex) {
1064
                                continue;
1065
                            }
1066
1067 3
                            $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
0 ignored issues
show
Bug introduced by
The variable $escherWorkbook 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...
1068 3
                            $BSE = $BSECollection[$BSEindex - 1];
1069 3
                            $blipType = $BSE->getBlipType();
1070
1071
                            // need check because some blip types are not supported by Escher reader such as EMF
1072 3
                            if ($blip = $BSE->getBlip()) {
1073 3
                                $ih = imagecreatefromstring($blip->getData());
1074 3
                                $drawing = new MemoryDrawing();
1075 3
                                $drawing->setImageResource($ih);
1076
1077
                                // width, height, offsetX, offsetY
1078 3
                                $drawing->setResizeProportional(false);
1079 3
                                $drawing->setWidth($width);
1080 3
                                $drawing->setHeight($height);
1081 3
                                $drawing->setOffsetX($offsetX);
1082 3
                                $drawing->setOffsetY($offsetY);
1083
1084
                                switch ($blipType) {
1085 3
                                    case BSE::BLIPTYPE_JPEG:
1086 3
                                        $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
1087 3
                                        $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
1088 3
                                        break;
1089 3
                                    case BSE::BLIPTYPE_PNG:
1090 3
                                        $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
1091 3
                                        $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
1092 3
                                        break;
1093
                                }
1094
1095 3
                                $drawing->setWorksheet($this->phpSheet);
1096 3
                                $drawing->setCoordinates($spContainer->getStartCoordinates());
1097
                            }
1098 3
                            break;
1099
                        default:
1100
                            // other object type
1101 3
                            break;
1102
                    }
1103
                }
1104
            }
1105
1106
            // treat SHAREDFMLA records
1107 4
            if ($this->version == self::XLS_BIFF8) {
1108 4
                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
1109
                    list($column, $row) = Cell::coordinateFromString($cell);
1110
                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
1111
                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
1112
                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, Cell\DataType::TYPE_FORMULA);
1113
                    }
1114
                }
1115
            }
1116
1117 4
            if (!empty($this->cellNotes)) {
1118 1
                foreach ($this->cellNotes as $note => $noteDetails) {
1119 1
                    if (!isset($noteDetails['objTextData'])) {
1120 View Code Duplication
                        if (isset($this->textObjects[$note])) {
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...
1121
                            $textObject = $this->textObjects[$note];
1122
                            $noteDetails['objTextData'] = $textObject;
1123
                        } else {
1124
                            $noteDetails['objTextData']['text'] = '';
1125
                        }
1126
                    }
1127 1
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
1128 4
                    $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
1129
                }
1130
            }
1131
        }
1132
1133
        // add the named ranges (defined names)
1134 4
        foreach ($this->definedname as $definedName) {
1135 1
            if ($definedName['isBuiltInName']) {
1136
                switch ($definedName['name']) {
1137
                    case pack('C', 0x06):
1138
                        // print area
1139
                        //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% 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...
1140
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1141
1142
                        $extractedRanges = [];
1143
                        foreach ($ranges as $range) {
1144
                            // $range should look like one of these
1145
                            //        Foo!$C$7:$J$66
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% 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...
1146
                            //        Bar!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% 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...
1147
                            $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
1148
                            $sheetName = trim($explodes[0], "'");
1149
                            if (count($explodes) == 2) {
1150
                                if (strpos($explodes[1], ':') === false) {
1151
                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
1152
                                }
1153
                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
1154
                            }
1155
                        }
1156
                        if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
0 ignored issues
show
Bug introduced by
The variable $sheetName 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...
1157
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
1158
                        }
1159
                        break;
1160
                    case pack('C', 0x07):
1161
                        // print titles (repeating rows)
1162
                        // Assuming BIFF8, there are 3 cases
1163
                        // 1. repeating rows
1164
                        //        formula looks like this: Sheet!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
1165
                        //        rows 1-2 repeat
1166
                        // 2. repeating columns
1167
                        //        formula looks like this: Sheet!$A$1:$B$65536
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
1168
                        //        columns A-B repeat
1169
                        // 3. both repeating rows and repeating columns
1170
                        //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% 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...
1171
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1172
                        foreach ($ranges as $range) {
1173
                            // $range should look like this one of these
1174
                            //        Sheet!$A$1:$B$65536
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% 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...
1175
                            //        Sheet!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% 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...
1176
                            $explodes = explode('!', $range);
1177
                            if (count($explodes) == 2) {
1178
                                if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
1179
                                    $extractedRange = $explodes[1];
1180
                                    $extractedRange = str_replace('$', '', $extractedRange);
1181
1182
                                    $coordinateStrings = explode(':', $extractedRange);
1183
                                    if (count($coordinateStrings) == 2) {
1184
                                        list($firstColumn, $firstRow) = Cell::coordinateFromString($coordinateStrings[0]);
1185
                                        list($lastColumn, $lastRow) = Cell::coordinateFromString($coordinateStrings[1]);
1186
1187
                                        if ($firstColumn == 'A' and $lastColumn == 'IV') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1188
                                            // then we have repeating rows
1189
                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
1190
                                        } elseif ($firstRow == 1 and $lastRow == 65536) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1191
                                            // then we have repeating columns
1192
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
1193
                                        }
1194
                                    }
1195
                                }
1196
                            }
1197
                        }
1198
                        break;
1199
                }
1200
            } else {
1201
                // Extract range
1202 1
                $explodes = explode('!', $definedName['formula']);
1203
1204 1
                if (count($explodes) == 2) {
1205 1
                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1206 1
                        ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $docSheet is correct as $this->spreadsheet->getS...im($explodes[0], '\'')) (which targets PhpOffice\PhpSpreadsheet...sheet::getSheetByName()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1207 1
                        $extractedRange = $explodes[1];
1208 1
                        $extractedRange = str_replace('$', '', $extractedRange);
1209
1210 1
                        $localOnly = ($definedName['scope'] == 0) ? false : true;
1211
1212 1
                        $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
1213
1214 1
                        $this->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
0 ignored issues
show
Bug introduced by
It seems like $docSheet can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1215
                    }
1216
                }
1217
                    //    Named Value
1218
                    //    TODO Provide support for named values
1219
            }
1220
        }
1221 4
        $this->data = null;
1222
1223 4
        return $this->spreadsheet;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->spreadsheet; (PhpOffice\PhpSpreadsheet\Spreadsheet) is incompatible with the return type declared by the interface PhpOffice\PhpSpreadsheet\Reader\IReader::load of type PhpOffice\PhpSpreadsheet\Reader\Spreadsheet.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1224
    }
1225
1226
    /**
1227
     * Read record data from stream, decrypting as required.
1228
     *
1229
     * @param string $data Data stream to read from
1230
     * @param int $pos Position to start reading from
1231
     * @param int $len Record data length
1232
     *
1233
     * @return string Record data
1234
     */
1235 4
    private function readRecordData($data, $pos, $len)
1236
    {
1237 4
        $data = substr($data, $pos, $len);
1238
1239
        // File not encrypted, or record before encryption start point
1240 4
        if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
1241 4
            return $data;
1242
        }
1243
1244
        $recordData = '';
1245
        if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
1246
            $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
1247
            $block = floor($pos / self::REKEY_BLOCK);
1248
            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
1249
1250
            // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
1251
            // at a point earlier in the current block, re-use it as we can save some time.
1252
            if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
1253
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1254
                $step = $pos % self::REKEY_BLOCK;
1255
            } else {
1256
                $step = $pos - $this->rc4Pos;
1257
            }
1258
            $this->rc4Key->RC4(str_repeat("\0", $step));
1259
1260
            // Decrypt record data (re-keying at the end of every block)
1261
            while ($block != $endBlock) {
1262
                $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
1263
                $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
1264
                $data = substr($data, $step);
1265
                $pos += $step;
1266
                $len -= $step;
1267
                ++$block;
1268
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1269
            }
1270
            $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
1271
1272
            // Keep track of the position of this decryptor.
1273
            // We'll try and re-use it later if we can to speed things up
1274
            $this->rc4Pos = $pos + $len;
1275
        } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
1276
            throw new Exception('XOr encryption not supported');
1277
        }
1278
1279
        return $recordData;
1280
    }
1281
1282
    /**
1283
     * Use OLE reader to extract the relevant data streams from the OLE file.
1284
     *
1285
     * @param string $pFilename
1286
     */
1287 4
    private function loadOLE($pFilename)
1288
    {
1289
        // OLE reader
1290 4
        $ole = new OLERead();
1291
        // get excel data,
1292 4
        $ole->read($pFilename);
1293
        // Get workbook data: workbook stream + sheet streams
1294 4
        $this->data = $ole->getStream($ole->wrkbook);
1295
        // Get summary information data
1296 4
        $this->summaryInformation = $ole->getStream($ole->summaryInformation);
1297
        // Get additional document summary information data
1298 4
        $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1299 4
    }
1300
1301
    /**
1302
     * Read summary information.
1303
     */
1304 4
    private function readSummaryInformation()
1305
    {
1306 4
        if (!isset($this->summaryInformation)) {
1307
            return;
1308
        }
1309
1310
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1311
        // offset: 2; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
1312
        // offset: 4; size: 2; OS version
1313
        // offset: 6; size: 2; OS indicator
1314
        // offset: 8; size: 16
1315
        // offset: 24; size: 4; section count
1316 4
        $secCount = self::getInt4d($this->summaryInformation, 24);
0 ignored issues
show
Unused Code introduced by
$secCount 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...
1317
1318
        // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
1319
        // offset: 44; size: 4
1320 4
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1321
1322
        // section header
1323
        // offset: $secOffset; size: 4; section length
1324 4
        $secLength = self::getInt4d($this->summaryInformation, $secOffset);
0 ignored issues
show
Unused Code introduced by
$secLength 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...
1325
1326
        // offset: $secOffset+4; size: 4; property count
1327 4
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1328
1329
        // initialize code page (used to resolve string values)
1330 4
        $codePage = 'CP1252';
1331
1332
        // offset: ($secOffset+8); size: var
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
1333
        // loop through property decarations and properties
1334 4
        for ($i = 0; $i < $countProperties; ++$i) {
1335
            // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
1336 4
            $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
1337
1338
            // Use value of property id as appropriate
1339
            // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
1340 4
            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
1341
1342 4
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1343
1344
            // initialize property value
1345 4
            $value = null;
1346
1347
            // extract property value based on property type
1348
            switch ($type) {
1349 4
                case 0x02: // 2 byte signed integer
1350 4
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1351 4
                    break;
1352 4
                case 0x03: // 4 byte signed integer
1353 3
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1354 3
                    break;
1355 4
                case 0x13: // 4 byte unsigned integer
1356
                    // not needed yet, fix later if necessary
1357
                    break;
1358 4 View Code Duplication
                case 0x1E: // null-terminated string prepended by dword string length
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...
1359 4
                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1360 4
                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1361 4
                    $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1362 4
                    $value = rtrim($value);
1363 4
                    break;
1364 4 View Code Duplication
                case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
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...
1365
                    // PHP-time
1366 4
                    $value = OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1367 4
                    break;
1368
                case 0x47: // Clipboard format
1369
                    // not needed yet, fix later if necessary
1370
                    break;
1371
            }
1372
1373
            switch ($id) {
1374 4
                case 0x01:    //    Code Page
1375 4
                    $codePage = CodePage::numberToName($value);
1376 4
                    break;
1377 4
                case 0x02:    //    Title
1378 3
                    $this->spreadsheet->getProperties()->setTitle($value);
1379 3
                    break;
1380 4
                case 0x03:    //    Subject
1381 3
                    $this->spreadsheet->getProperties()->setSubject($value);
1382 3
                    break;
1383 4
                case 0x04:    //    Author (Creator)
1384 4
                    $this->spreadsheet->getProperties()->setCreator($value);
1385 4
                    break;
1386 4
                case 0x05:    //    Keywords
1387 3
                    $this->spreadsheet->getProperties()->setKeywords($value);
1388 3
                    break;
1389 4
                case 0x06:    //    Comments (Description)
1390 3
                    $this->spreadsheet->getProperties()->setDescription($value);
1391 3
                    break;
1392 4
                case 0x07:    //    Template
1393
                    //    Not supported by PhpSpreadsheet
1394
                    break;
1395 4
                case 0x08:    //    Last Saved By (LastModifiedBy)
1396 3
                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
1397 3
                    break;
1398 4
                case 0x09:    //    Revision
1399
                    //    Not supported by PhpSpreadsheet
1400 1
                    break;
1401 4
                case 0x0A:    //    Total Editing Time
1402
                    //    Not supported by PhpSpreadsheet
1403 1
                    break;
1404 4
                case 0x0B:    //    Last Printed
1405
                    //    Not supported by PhpSpreadsheet
1406 1
                    break;
1407 4
                case 0x0C:    //    Created Date/Time
1408 4
                    $this->spreadsheet->getProperties()->setCreated($value);
1409 4
                    break;
1410 4
                case 0x0D:    //    Modified Date/Time
1411 4
                    $this->spreadsheet->getProperties()->setModified($value);
1412 4
                    break;
1413 3
                case 0x0E:    //    Number of Pages
1414
                    //    Not supported by PhpSpreadsheet
1415
                    break;
1416 3
                case 0x0F:    //    Number of Words
1417
                    //    Not supported by PhpSpreadsheet
1418
                    break;
1419 3
                case 0x10:    //    Number of Characters
1420
                    //    Not supported by PhpSpreadsheet
1421
                    break;
1422 3
                case 0x11:    //    Thumbnail
1423
                    //    Not supported by PhpSpreadsheet
1424
                    break;
1425 3
                case 0x12:    //    Name of creating application
1426
                    //    Not supported by PhpSpreadsheet
1427 1
                    break;
1428 3
                case 0x13:    //    Security
1429
                    //    Not supported by PhpSpreadsheet
1430 3
                    break;
1431
            }
1432
        }
1433 4
    }
1434
1435
    /**
1436
     * Read additional document summary information.
1437
     */
1438 4
    private function readDocumentSummaryInformation()
1439
    {
1440 4
        if (!isset($this->documentSummaryInformation)) {
1441
            return;
1442
        }
1443
1444
        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
1445
        //    offset: 2;    size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
1446
        //    offset: 4;    size: 2;    OS version
1447
        //    offset: 6;    size: 2;    OS indicator
1448
        //    offset: 8;    size: 16
1449
        //    offset: 24;    size: 4;    section count
1450 4
        $secCount = self::getInt4d($this->documentSummaryInformation, 24);
0 ignored issues
show
Unused Code introduced by
$secCount 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...
1451
1452
        // offset: 28;    size: 16;    first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
1453
        // offset: 44;    size: 4;    first section offset
1454 4
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1455
1456
        //    section header
1457
        //    offset: $secOffset;    size: 4;    section length
1458 4
        $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
0 ignored issues
show
Unused Code introduced by
$secLength 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...
1459
1460
        //    offset: $secOffset+4;    size: 4;    property count
1461 4
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1462
1463
        // initialize code page (used to resolve string values)
1464 4
        $codePage = 'CP1252';
1465
1466
        //    offset: ($secOffset+8);    size: var
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
1467
        //    loop through property decarations and properties
1468 4
        for ($i = 0; $i < $countProperties; ++$i) {
1469
            //    offset: ($secOffset+8) + (8 * $i);    size: 4;    property ID
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
1470 4
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
1471
1472
            // Use value of property id as appropriate
1473
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1474 4
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
1475
1476 4
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1477
1478
            // initialize property value
1479 4
            $value = null;
1480
1481
            // extract property value based on property type
1482
            switch ($type) {
1483 4
                case 0x02:    //    2 byte signed integer
1484 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1485 4
                    break;
1486 3
                case 0x03:    //    4 byte signed integer
1487 3
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1488 3
                    break;
1489 3
                case 0x0B:  // Boolean
1490 3
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1491 3
                    $value = ($value == 0 ? false : true);
1492 3
                    break;
1493 3
                case 0x13:    //    4 byte unsigned integer
1494
                    // not needed yet, fix later if necessary
1495
                    break;
1496 3 View Code Duplication
                case 0x1E:    //    null-terminated string prepended by dword string length
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...
1497 3
                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1498 3
                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1499 3
                    $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1500 3
                    $value = rtrim($value);
1501 3
                    break;
1502 3 View Code Duplication
                case 0x40:    //    Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
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...
1503
                    // PHP-Time
1504
                    $value = OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1505
                    break;
1506 3
                case 0x47:    //    Clipboard format
1507
                    // not needed yet, fix later if necessary
1508
                    break;
1509
            }
1510
1511
            switch ($id) {
1512 4
                case 0x01:    //    Code Page
1513 4
                    $codePage = CodePage::numberToName($value);
1514 4
                    break;
1515 3
                case 0x02:    //    Category
1516 3
                    $this->spreadsheet->getProperties()->setCategory($value);
1517 3
                    break;
1518 3
                case 0x03:    //    Presentation Target
1519
                    //    Not supported by PhpSpreadsheet
1520
                    break;
1521 3
                case 0x04:    //    Bytes
1522
                    //    Not supported by PhpSpreadsheet
1523
                    break;
1524 3
                case 0x05:    //    Lines
1525
                    //    Not supported by PhpSpreadsheet
1526
                    break;
1527 3
                case 0x06:    //    Paragraphs
1528
                    //    Not supported by PhpSpreadsheet
1529
                    break;
1530 3
                case 0x07:    //    Slides
1531
                    //    Not supported by PhpSpreadsheet
1532
                    break;
1533 3
                case 0x08:    //    Notes
1534
                    //    Not supported by PhpSpreadsheet
1535
                    break;
1536 3
                case 0x09:    //    Hidden Slides
1537
                    //    Not supported by PhpSpreadsheet
1538
                    break;
1539 3
                case 0x0A:    //    MM Clips
1540
                    //    Not supported by PhpSpreadsheet
1541
                    break;
1542 3
                case 0x0B:    //    Scale Crop
1543
                    //    Not supported by PhpSpreadsheet
1544 3
                    break;
1545 3
                case 0x0C:    //    Heading Pairs
1546
                    //    Not supported by PhpSpreadsheet
1547 3
                    break;
1548 3
                case 0x0D:    //    Titles of Parts
1549
                    //    Not supported by PhpSpreadsheet
1550 3
                    break;
1551 3
                case 0x0E:    //    Manager
1552 1
                    $this->spreadsheet->getProperties()->setManager($value);
1553 1
                    break;
1554 3
                case 0x0F:    //    Company
1555 2
                    $this->spreadsheet->getProperties()->setCompany($value);
1556 2
                    break;
1557 3
                case 0x10:    //    Links up-to-date
1558
                    //    Not supported by PhpSpreadsheet
1559 3
                    break;
1560
            }
1561
        }
1562 4
    }
1563
1564
    /**
1565
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1566
     */
1567 4
    private function readDefault()
1568
    {
1569 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1570
1571
        // move stream pointer to next record
1572 4
        $this->pos += 4 + $length;
1573 4
    }
1574
1575
    /**
1576
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1577
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1578
     */
1579 1
    private function readNote()
1580
    {
1581 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1582 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1583
1584
        // move stream pointer to next record
1585 1
        $this->pos += 4 + $length;
1586
1587 1
        if ($this->readDataOnly) {
1588
            return;
1589
        }
1590
1591 1
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1592 1
        if ($this->version == self::XLS_BIFF8) {
1593 1
            $noteObjID = self::getInt2d($recordData, 6);
1594 1
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1595 1
            $noteAuthor = $noteAuthor['value'];
1596 1
            $this->cellNotes[$noteObjID] = [
1597 1
                'cellRef' => $cellAddress,
1598 1
                'objectID' => $noteObjID,
1599 1
                'author' => $noteAuthor,
1600
            ];
1601
        } else {
1602
            $extension = false;
1603
            if ($cellAddress == '$B$65536') {
1604
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1605
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1606
                //        max 2048 bytes will probably throw a wobbly.
1607
                $row = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$row 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...
1608
                $extension = true;
1609
                $cellAddress = array_pop(array_keys($this->phpSheet->getComments()));
0 ignored issues
show
Bug introduced by
array_keys($this->phpSheet->getComments()) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
1610
            }
1611
1612
            $cellAddress = str_replace('$', '', $cellAddress);
1613
            $noteLength = self::getInt2d($recordData, 4);
0 ignored issues
show
Unused Code introduced by
$noteLength 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...
1614
            $noteText = trim(substr($recordData, 6));
1615
1616
            if ($extension) {
1617
                //    Concatenate this extension with the currently set comment for the cell
1618
                $comment = $this->phpSheet->getComment($cellAddress);
1619
                $commentText = $comment->getText()->getPlainText();
1620
                $comment->setText($this->parseRichText($commentText . $noteText));
1621
            } else {
1622
                //    Set comment for the cell
1623
                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
1624
//                                                    ->setAuthor($author)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
1625
            }
1626
        }
1627 1
    }
1628
1629
    /**
1630
     * The TEXT Object record contains the text associated with a cell annotation.
1631
     */
1632 1
    private function readTextObject()
1633
    {
1634 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1635 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1636
1637
        // move stream pointer to next record
1638 1
        $this->pos += 4 + $length;
1639
1640 1
        if ($this->readDataOnly) {
1641
            return;
1642
        }
1643
1644
        // recordData consists of an array of subrecords looking like this:
1645
        //    grbit: 2 bytes; Option Flags
1646
        //    rot: 2 bytes; rotation
1647
        //    cchText: 2 bytes; length of the text (in the first continue record)
1648
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1649
        // followed by the continuation records containing the actual text and formatting
1650 1
        $grbitOpts = self::getInt2d($recordData, 0);
1651 1
        $rot = self::getInt2d($recordData, 2);
1652 1
        $cchText = self::getInt2d($recordData, 10);
0 ignored issues
show
Unused Code introduced by
$cchText 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...
1653 1
        $cbRuns = self::getInt2d($recordData, 12);
1654 1
        $text = $this->getSplicedRecordData();
1655
1656 1
        $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1;
1657 1
        $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte);
1658
        // get 1 byte
1659 1
        $is16Bit = ord($text['recordData'][0]);
1660
        // it is possible to use a compressed format,
1661
        // which omits the high bytes of all characters, if they are all zero
1662 1
        if (($is16Bit & 0x01) === 0) {
1663 1
            $textStr = StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1');
1664
        } else {
1665
            $textStr = $this->decodeCodepage($textStr);
1666
        }
1667
1668 1
        $this->textObjects[$this->textObjRef] = [
1669 1
            'text' => $textStr,
1670 1
            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
1671 1
            'alignment' => $grbitOpts,
1672 1
            'rotation' => $rot,
1673
        ];
1674 1
    }
1675
1676
    /**
1677
     * Read BOF.
1678
     */
1679 4
    private function readBof()
1680
    {
1681 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1682 4
        $recordData = substr($this->data, $this->pos + 4, $length);
1683
1684
        // move stream pointer to next record
1685 4
        $this->pos += 4 + $length;
1686
1687
        // offset: 2; size: 2; type of the following data
1688 4
        $substreamType = self::getInt2d($recordData, 2);
1689
1690
        switch ($substreamType) {
1691 4
            case self::XLS_WORKBOOKGLOBALS:
1692 4
                $version = self::getInt2d($recordData, 0);
1693 4
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1694
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1695
                }
1696 4
                $this->version = $version;
1697 4
                break;
1698 4
            case self::XLS_WORKSHEET:
1699
                // do not use this version information for anything
1700
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1701 4
                break;
1702
            default:
1703
                // substream, e.g. chart
1704
                // just skip the entire substream
1705
                do {
1706
                    $code = self::getInt2d($this->data, $this->pos);
1707
                    $this->readDefault();
1708
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1709
                break;
1710
        }
1711 4
    }
1712
1713
    /**
1714
     * FILEPASS.
1715
     *
1716
     * This record is part of the File Protection Block. It
1717
     * contains information about the read/write password of the
1718
     * file. All record contents following this record will be
1719
     * encrypted.
1720
     *
1721
     * --    "OpenOffice.org's Documentation of the Microsoft
1722
     *         Excel File Format"
1723
     *
1724
     * The decryption functions and objects used from here on in
1725
     * are based on the source of Spreadsheet-ParseExcel:
1726
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1727
     */
1728
    private function readFilepass()
1729
    {
1730
        $length = self::getInt2d($this->data, $this->pos + 2);
1731
1732
        if ($length != 54) {
1733
            throw new Exception('Unexpected file pass record length');
1734
        }
1735
1736
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1737
1738
        // move stream pointer to next record
1739
        $this->pos += 4 + $length;
1740
1741
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1742
            throw new Exception('Decryption password incorrect');
1743
        }
1744
1745
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1746
1747
        // Decryption required from the record after next onwards
1748
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1749
    }
1750
1751
    /**
1752
     * Make an RC4 decryptor for the given block.
1753
     *
1754
     * @param int Block for which to create decrypto
1755
     * @param string $valContext MD5 context state
1756
     * @param mixed $block
1757
     *
1758
     * @return Xls\RC4
1759
     */
1760
    private function makeKey($block, $valContext)
1761
    {
1762
        $pwarray = str_repeat("\0", 64);
1763
1764
        for ($i = 0; $i < 5; ++$i) {
1765
            $pwarray[$i] = $valContext[$i];
1766
        }
1767
1768
        $pwarray[5] = chr($block & 0xff);
1769
        $pwarray[6] = chr(($block >> 8) & 0xff);
1770
        $pwarray[7] = chr(($block >> 16) & 0xff);
1771
        $pwarray[8] = chr(($block >> 24) & 0xff);
1772
1773
        $pwarray[9] = "\x80";
1774
        $pwarray[56] = "\x48";
1775
1776
        $md5 = new Xls\MD5();
1777
        $md5->add($pwarray);
1778
1779
        $s = $md5->getContext();
1780
1781
        return new Xls\RC4($s);
1782
    }
1783
1784
    /**
1785
     * Verify RC4 file password.
1786
     *
1787
     * @param string $password Password to check
1788
     * @param string $docid Document id
1789
     * @param string $salt_data Salt data
1790
     * @param string $hashedsalt_data Hashed salt data
1791
     * @param string $valContext Set to the MD5 context of the value
1792
     *
1793
     * @return bool Success
1794
     */
1795
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1796
    {
1797
        $pwarray = str_repeat("\0", 64);
1798
1799
        for ($i = 0; $i < strlen($password); ++$i) {
1800
            $o = ord(substr($password, $i, 1));
1801
            $pwarray[2 * $i] = chr($o & 0xff);
1802
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1803
        }
1804
        $pwarray[2 * $i] = chr(0x80);
1805
        $pwarray[56] = chr(($i << 4) & 0xff);
1806
1807
        $md5 = new Xls\MD5();
1808
        $md5->add($pwarray);
1809
1810
        $mdContext1 = $md5->getContext();
1811
1812
        $offset = 0;
1813
        $keyoffset = 0;
1814
        $tocopy = 5;
1815
1816
        $md5->reset();
1817
1818
        while ($offset != 16) {
1819
            if ((64 - $offset) < 5) {
1820
                $tocopy = 64 - $offset;
1821
            }
1822
            for ($i = 0; $i <= $tocopy; ++$i) {
1823
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1824
            }
1825
            $offset += $tocopy;
1826
1827
            if ($offset == 64) {
1828
                $md5->add($pwarray);
1829
                $keyoffset = $tocopy;
1830
                $tocopy = 5 - $tocopy;
1831
                $offset = 0;
1832
                continue;
1833
            }
1834
1835
            $keyoffset = 0;
1836
            $tocopy = 5;
1837
            for ($i = 0; $i < 16; ++$i) {
1838
                $pwarray[$offset + $i] = $docid[$i];
1839
            }
1840
            $offset += 16;
1841
        }
1842
1843
        $pwarray[16] = "\x80";
1844
        for ($i = 0; $i < 47; ++$i) {
1845
            $pwarray[17 + $i] = "\0";
1846
        }
1847
        $pwarray[56] = "\x80";
1848
        $pwarray[57] = "\x0a";
1849
1850
        $md5->add($pwarray);
1851
        $valContext = $md5->getContext();
1852
1853
        $key = $this->makeKey(0, $valContext);
1854
1855
        $salt = $key->RC4($salt_data);
1856
        $hashedsalt = $key->RC4($hashedsalt_data);
1857
1858
        $salt .= "\x80" . str_repeat("\0", 47);
1859
        $salt[56] = "\x80";
1860
1861
        $md5->reset();
1862
        $md5->add($salt);
1863
        $mdContext2 = $md5->getContext();
1864
1865
        return $mdContext2 == $hashedsalt;
1866
    }
1867
1868
    /**
1869
     * CODEPAGE.
1870
     *
1871
     * This record stores the text encoding used to write byte
1872
     * strings, stored as MS Windows code page identifier.
1873
     *
1874
     * --    "OpenOffice.org's Documentation of the Microsoft
1875
     *         Excel File Format"
1876
     */
1877 4 View Code Duplication
    private function readCodepage()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1878
    {
1879 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1880 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1881
1882
        // move stream pointer to next record
1883 4
        $this->pos += 4 + $length;
1884
1885
        // offset: 0; size: 2; code page identifier
1886 4
        $codepage = self::getInt2d($recordData, 0);
1887
1888 4
        $this->codepage = CodePage::numberToName($codepage);
1889 4
    }
1890
1891
    /**
1892
     * DATEMODE.
1893
     *
1894
     * This record specifies the base date for displaying date
1895
     * values. All dates are stored as count of days past this
1896
     * base date. In BIFF2-BIFF4 this record is part of the
1897
     * Calculation Settings Block. In BIFF5-BIFF8 it is
1898
     * stored in the Workbook Globals Substream.
1899
     *
1900
     * --    "OpenOffice.org's Documentation of the Microsoft
1901
     *         Excel File Format"
1902
     */
1903 4
    private function readDateMode()
1904
    {
1905 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1906 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1907
1908
        // move stream pointer to next record
1909 4
        $this->pos += 4 + $length;
1910
1911
        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
1912 4
        Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
1913 4
        if (ord($recordData[0]) == 1) {
1914
            Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
1915
        }
1916 4
    }
1917
1918
    /**
1919
     * Read a FONT record.
1920
     */
1921 4
    private function readFont()
1922
    {
1923 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1924 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1925
1926
        // move stream pointer to next record
1927 4
        $this->pos += 4 + $length;
1928
1929 4
        if (!$this->readDataOnly) {
1930 4
            $objFont = new Font();
1931
1932
            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
1933 4
            $size = self::getInt2d($recordData, 0);
1934 4
            $objFont->setSize($size / 20);
1935
1936
            // offset: 2; size: 2; option flags
1937
            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
1938
            // bit: 1; mask 0x0002; italic
1939 4
            $isItalic = (0x0002 & self::getInt2d($recordData, 2)) >> 1;
1940 4
            if ($isItalic) {
1941 4
                $objFont->setItalic(true);
1942
            }
1943
1944
            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
1945
            // bit: 3; mask 0x0008; strike
1946 4
            $isStrike = (0x0008 & self::getInt2d($recordData, 2)) >> 3;
1947 4
            if ($isStrike) {
1948
                $objFont->setStrikethrough(true);
1949
            }
1950
1951
            // offset: 4; size: 2; colour index
1952 4
            $colorIndex = self::getInt2d($recordData, 4);
1953 4
            $objFont->colorIndex = $colorIndex;
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpOffice\PhpSpreadsheet\Style\Font.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1954
1955
            // offset: 6; size: 2; font weight
1956 4
            $weight = self::getInt2d($recordData, 6);
1957
            switch ($weight) {
1958 4
                case 0x02BC:
1959 4
                    $objFont->setBold(true);
1960 4
                    break;
1961
            }
1962
1963
            // offset: 8; size: 2; escapement type
1964 4
            $escapement = self::getInt2d($recordData, 8);
1965
            switch ($escapement) {
1966 4
                case 0x0001:
1967
                    $objFont->setSuperScript(true);
1968
                    break;
1969 4
                case 0x0002:
1970
                    $objFont->setSubScript(true);
1971
                    break;
1972
            }
1973
1974
            // offset: 10; size: 1; underline type
1975 4
            $underlineType = ord($recordData[10]);
1976
            switch ($underlineType) {
1977 4
                case 0x00:
1978 4
                    break; // no underline
1979 2
                case 0x01:
1980 2
                    $objFont->setUnderline(Font::UNDERLINE_SINGLE);
1981 2
                    break;
1982
                case 0x02:
1983
                    $objFont->setUnderline(Font::UNDERLINE_DOUBLE);
1984
                    break;
1985
                case 0x21:
1986
                    $objFont->setUnderline(Font::UNDERLINE_SINGLEACCOUNTING);
1987
                    break;
1988
                case 0x22:
1989
                    $objFont->setUnderline(Font::UNDERLINE_DOUBLEACCOUNTING);
1990
                    break;
1991
            }
1992
1993
            // offset: 11; size: 1; font family
1994
            // offset: 12; size: 1; character set
1995
            // offset: 13; size: 1; not used
1996
            // offset: 14; size: var; font name
1997 4 View Code Duplication
            if ($this->version == self::XLS_BIFF8) {
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...
1998 4
                $string = self::readUnicodeStringShort(substr($recordData, 14));
1999
            } else {
2000
                $string = $this->readByteStringShort(substr($recordData, 14));
2001
            }
2002 4
            $objFont->setName($string['value']);
2003
2004 4
            $this->objFonts[] = $objFont;
2005
        }
2006 4
    }
2007
2008
    /**
2009
     * FORMAT.
2010
     *
2011
     * This record contains information about a number format.
2012
     * All FORMAT records occur together in a sequential list.
2013
     *
2014
     * In BIFF2-BIFF4 other records referencing a FORMAT record
2015
     * contain a zero-based index into this list. From BIFF5 on
2016
     * the FORMAT record contains the index itself that will be
2017
     * used by other records.
2018
     *
2019
     * --    "OpenOffice.org's Documentation of the Microsoft
2020
     *         Excel File Format"
2021
     */
2022 4
    private function readFormat()
2023
    {
2024 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2025 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2026
2027
        // move stream pointer to next record
2028 4
        $this->pos += 4 + $length;
2029
2030 4
        if (!$this->readDataOnly) {
2031 4
            $indexCode = self::getInt2d($recordData, 0);
2032
2033 4 View Code Duplication
            if ($this->version == self::XLS_BIFF8) {
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...
2034 4
                $string = self::readUnicodeStringLong(substr($recordData, 2));
2035
            } else {
2036
                // BIFF7
2037
                $string = $this->readByteStringShort(substr($recordData, 2));
2038
            }
2039
2040 4
            $formatString = $string['value'];
2041 4
            $this->formats[$indexCode] = $formatString;
2042
        }
2043 4
    }
2044
2045
    /**
2046
     * XF - Extended Format.
2047
     *
2048
     * This record contains formatting information for cells, rows, columns or styles.
2049
     * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
2050
     * and 1 cell XF.
2051
     * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
2052
     * and XF record 15 is a cell XF
2053
     * We only read the first cell style XF and skip the remaining cell style XF records
2054
     * We read all cell XF records.
2055
     *
2056
     * --    "OpenOffice.org's Documentation of the Microsoft
2057
     *         Excel File Format"
2058
     */
2059 4
    private function readXf()
2060
    {
2061 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2062 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2063
2064
        // move stream pointer to next record
2065 4
        $this->pos += 4 + $length;
2066
2067 4
        $objStyle = new Style();
2068
2069 4
        if (!$this->readDataOnly) {
2070
            // offset:  0; size: 2; Index to FONT record
2071 4
            if (self::getInt2d($recordData, 0) < 4) {
2072 4
                $fontIndex = self::getInt2d($recordData, 0);
2073
            } else {
2074
                // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
2075
                // check the OpenOffice documentation of the FONT record
2076 4
                $fontIndex = self::getInt2d($recordData, 0) - 1;
2077
            }
2078 4
            $objStyle->setFont($this->objFonts[$fontIndex]);
2079
2080
            // offset:  2; size: 2; Index to FORMAT record
2081 4
            $numberFormatIndex = self::getInt2d($recordData, 2);
2082 4
            if (isset($this->formats[$numberFormatIndex])) {
2083
                // then we have user-defined format code
2084 3
                $numberformat = ['code' => $this->formats[$numberFormatIndex]];
2085 4
            } elseif (($code = NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
2086
                // then we have built-in format code
2087 4
                $numberformat = ['code' => $code];
2088
            } else {
2089
                // we set the general format code
2090 1
                $numberformat = ['code' => 'General'];
2091
            }
2092 4
            $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
2093
2094
            // offset:  4; size: 2; XF type, cell protection, and parent style XF
2095
            // bit 2-0; mask 0x0007; XF_TYPE_PROT
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
2096 4
            $xfTypeProt = self::getInt2d($recordData, 4);
2097
            // bit 0; mask 0x01; 1 = cell is locked
2098 4
            $isLocked = (0x01 & $xfTypeProt) >> 0;
2099 4
            $objStyle->getProtection()->setLocked($isLocked ? Protection::PROTECTION_INHERIT : Protection::PROTECTION_UNPROTECTED);
2100
2101
            // bit 1; mask 0x02; 1 = Formula is hidden
2102 4
            $isHidden = (0x02 & $xfTypeProt) >> 1;
2103 4
            $objStyle->getProtection()->setHidden($isHidden ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED);
2104
2105
            // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
2106 4
            $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
2107
2108
            // offset:  6; size: 1; Alignment and text break
2109
            // bit 2-0, mask 0x07; horizontal alignment
2110 4
            $horAlign = (0x07 & ord($recordData[6])) >> 0;
2111
            switch ($horAlign) {
2112 4
                case 0:
2113 4
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_GENERAL);
2114 4
                    break;
2115 2
                case 1:
2116 2
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
2117 2
                    break;
2118 2
                case 2:
2119
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
2120
                    break;
2121 2
                case 3:
2122 2
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
2123 2
                    break;
2124 2
                case 4:
2125
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_FILL);
2126
                    break;
2127 2
                case 5:
2128 2
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_JUSTIFY);
2129 2
                    break;
2130
                case 6:
2131
                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER_CONTINUOUS);
2132
                    break;
2133
            }
2134
            // bit 3, mask 0x08; wrap text
2135 4
            $wrapText = (0x08 & ord($recordData[6])) >> 3;
2136
            switch ($wrapText) {
2137 4
                case 0:
2138 4
                    $objStyle->getAlignment()->setWrapText(false);
2139 4
                    break;
2140 2
                case 1:
2141 2
                    $objStyle->getAlignment()->setWrapText(true);
2142 2
                    break;
2143
            }
2144
            // bit 6-4, mask 0x70; vertical alignment
2145 4
            $vertAlign = (0x70 & ord($recordData[6])) >> 4;
2146
            switch ($vertAlign) {
2147 4
                case 0:
2148
                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_TOP);
2149
                    break;
2150 4
                case 1:
2151 2
                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
2152 2
                    break;
2153 4
                case 2:
2154 4
                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM);
2155 4
                    break;
2156
                case 3:
2157
                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_JUSTIFY);
2158
                    break;
2159
            }
2160
2161 4
            if ($this->version == self::XLS_BIFF8) {
2162
                // offset:  7; size: 1; XF_ROTATION: Text rotation angle
2163 4
                $angle = ord($recordData[7]);
2164 4
                $rotation = 0;
2165 4
                if ($angle <= 90) {
2166 4
                    $rotation = $angle;
2167
                } elseif ($angle <= 180) {
2168
                    $rotation = 90 - $angle;
2169
                } elseif ($angle == 255) {
2170
                    $rotation = -165;
2171
                }
2172 4
                $objStyle->getAlignment()->setTextRotation($rotation);
2173
2174
                // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
2175
                // bit: 3-0; mask: 0x0F; indent level
2176 4
                $indent = (0x0F & ord($recordData[8])) >> 0;
2177 4
                $objStyle->getAlignment()->setIndent($indent);
2178
2179
                // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
2180 4
                $shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
2181
                switch ($shrinkToFit) {
2182 4
                    case 0:
2183 4
                        $objStyle->getAlignment()->setShrinkToFit(false);
2184 4
                        break;
2185 1
                    case 1:
2186 1
                        $objStyle->getAlignment()->setShrinkToFit(true);
2187 1
                        break;
2188
                }
2189
2190
                // offset:  9; size: 1; Flags used for attribute groups
2191
2192
                // offset: 10; size: 4; Cell border lines and background area
2193
                // bit: 3-0; mask: 0x0000000F; left style
2194 4 View Code Duplication
                if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
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...
2195 4
                    $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
2196
                }
2197
                // bit: 7-4; mask: 0x000000F0; right style
2198 4 View Code Duplication
                if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
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...
2199 4
                    $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
2200
                }
2201
                // bit: 11-8; mask: 0x00000F00; top style
2202 4 View Code Duplication
                if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
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...
2203 4
                    $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
2204
                }
2205
                // bit: 15-12; mask: 0x0000F000; bottom style
2206 4 View Code Duplication
                if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
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...
2207 4
                    $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
2208
                }
2209
                // bit: 22-16; mask: 0x007F0000; left color
2210 4
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpOffice\PhpSpreadsheet\Style\Border.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2211
2212
                // bit: 29-23; mask: 0x3F800000; right color
2213 4
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2214
2215
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2216 4
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2217
2218
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2219 4
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2220
2221 4
                if ($diagonalUp == false && $diagonalDown == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2222 4
                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
2223
                } elseif ($diagonalUp == true && $diagonalDown == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2224
                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
2225
                } elseif ($diagonalUp == false && $diagonalDown == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2226
                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
2227
                } elseif ($diagonalUp == true && $diagonalDown == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2228
                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
2229
                }
2230
2231
                // offset: 14; size: 4;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
2232
                // bit: 6-0; mask: 0x0000007F; top color
2233 4
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2234
2235
                // bit: 13-7; mask: 0x00003F80; bottom color
2236 4
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2237
2238
                // bit: 20-14; mask: 0x001FC000; diagonal color
2239 4
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2240
2241
                // bit: 24-21; mask: 0x01E00000; diagonal style
2242 4 View Code Duplication
                if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
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...
2243 4
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2244
                }
2245
2246
                // bit: 31-26; mask: 0xFC000000 fill pattern
2247 4
                if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2248 4
                    $objStyle->getFill()->setFillType($fillType);
2249
                }
2250
                // offset: 18; size: 2; pattern and background colour
2251
                // bit: 6-0; mask: 0x007F; color index for pattern color
2252 4
                $objStyle->getFill()->startcolorIndex = (0x007F & self::getInt2d($recordData, 18)) >> 0;
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2253
2254
                // bit: 13-7; mask: 0x3F80; color index for pattern background
2255 4
                $objStyle->getFill()->endcolorIndex = (0x3F80 & self::getInt2d($recordData, 18)) >> 7;
0 ignored issues
show
Bug introduced by
The property endcolorIndex does not seem to exist. Did you mean endColor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2256
            } else {
2257
                // BIFF5
2258
2259
                // offset: 7; size: 1; Text orientation and flags
2260
                $orientationAndFlags = ord($recordData[7]);
2261
2262
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2263
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2264
                switch ($xfOrientation) {
2265
                    case 0:
2266
                        $objStyle->getAlignment()->setTextRotation(0);
2267
                        break;
2268
                    case 1:
2269
                        $objStyle->getAlignment()->setTextRotation(-165);
2270
                        break;
2271
                    case 2:
2272
                        $objStyle->getAlignment()->setTextRotation(90);
2273
                        break;
2274
                    case 3:
2275
                        $objStyle->getAlignment()->setTextRotation(-90);
2276
                        break;
2277
                }
2278
2279
                // offset: 8; size: 4; cell border lines and background area
2280
                $borderAndBackground = self::getInt4d($recordData, 8);
2281
2282
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2283
                $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2284
2285
                // bit: 13-7; mask: 0x00003F80; color index for pattern background
2286
                $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
0 ignored issues
show
Bug introduced by
The property endcolorIndex does not seem to exist. Did you mean endColor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2287
2288
                // bit: 21-16; mask: 0x003F0000; fill pattern
2289
                $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2290
2291
                // bit: 24-22; mask: 0x01C00000; bottom line style
2292
                $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2293
2294
                // bit: 31-25; mask: 0xFE000000; bottom line color
2295
                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2296
2297
                // offset: 12; size: 4; cell border lines
2298
                $borderLines = self::getInt4d($recordData, 12);
2299
2300
                // bit: 2-0; mask: 0x00000007; top line style
2301
                $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2302
2303
                // bit: 5-3; mask: 0x00000038; left line style
2304
                $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2305
2306
                // bit: 8-6; mask: 0x000001C0; right line style
2307
                $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2308
2309
                // bit: 15-9; mask: 0x0000FE00; top line color index
2310
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2311
2312
                // bit: 22-16; mask: 0x007F0000; left line color index
2313
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2314
2315
                // bit: 29-23; mask: 0x3F800000; right line color index
2316
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2317
            }
2318
2319
            // add cellStyleXf or cellXf and update mapping
2320 4
            if ($isCellStyleXf) {
2321
                // we only read one style XF record which is always the first
2322 4
                if ($this->xfIndex == 0) {
2323 4
                    $this->spreadsheet->addCellStyleXf($objStyle);
2324 4
                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
2325
                }
2326
            } else {
2327
                // we read all cell XF records
2328 4
                $this->spreadsheet->addCellXf($objStyle);
2329 4
                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2330
            }
2331
2332
            // update XF index for when we read next record
2333 4
            ++$this->xfIndex;
2334
        }
2335 4
    }
2336
2337 2
    private function readXfExt()
2338
    {
2339 2
        $length = self::getInt2d($this->data, $this->pos + 2);
2340 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2341
2342
        // move stream pointer to next record
2343 2
        $this->pos += 4 + $length;
2344
2345 2
        if (!$this->readDataOnly) {
2346
            // offset: 0; size: 2; 0x087D = repeated header
2347
2348
            // offset: 2; size: 2
2349
2350
            // offset: 4; size: 8; not used
2351
2352
            // offset: 12; size: 2; record version
2353
2354
            // offset: 14; size: 2; index to XF record which this record modifies
2355 2
            $ixfe = self::getInt2d($recordData, 14);
2356
2357
            // offset: 16; size: 2; not used
2358
2359
            // offset: 18; size: 2; number of extension properties that follow
2360 2
            $cexts = self::getInt2d($recordData, 18);
0 ignored issues
show
Unused Code introduced by
$cexts 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...
2361
2362
            // start reading the actual extension data
2363 2
            $offset = 20;
2364 2
            while ($offset < $length) {
2365
                // extension type
2366 2
                $extType = self::getInt2d($recordData, $offset);
2367
2368
                // extension length
2369 2
                $cb = self::getInt2d($recordData, $offset + 2);
2370
2371
                // extension data
2372 2
                $extData = substr($recordData, $offset + 4, $cb);
2373
2374
                switch ($extType) {
2375 2 View Code Duplication
                    case 4:        // fill start color
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...
2376 2
                        $xclfType = self::getInt2d($extData, 0); // color type
2377 2
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2378
2379 2
                        if ($xclfType == 2) {
2380 2
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2381
2382
                            // modify the relevant style property
2383 2
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2384 1
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2385 1
                                $fill->getStartColor()->setRGB($rgb);
2386 1
                                unset($fill->startcolorIndex); // normal color index does not apply, discard
2387
                            }
2388
                        }
2389 2
                        break;
2390 2 View Code Duplication
                    case 5:        // fill end color
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...
2391 1
                        $xclfType = self::getInt2d($extData, 0); // color type
2392 1
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2393
2394 1
                        if ($xclfType == 2) {
2395 1
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2396
2397
                            // modify the relevant style property
2398 1
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2399 1
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2400 1
                                $fill->getEndColor()->setRGB($rgb);
2401 1
                                unset($fill->endcolorIndex); // normal color index does not apply, discard
2402
                            }
2403
                        }
2404 1
                        break;
2405 2 View Code Duplication
                    case 7:        // border color top
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...
2406 2
                        $xclfType = self::getInt2d($extData, 0); // color type
2407 2
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2408
2409 2
                        if ($xclfType == 2) {
2410 2
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2411
2412
                            // modify the relevant style property
2413 2
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2414 1
                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2415 1
                                $top->getColor()->setRGB($rgb);
2416 1
                                unset($top->colorIndex); // normal color index does not apply, discard
2417
                            }
2418
                        }
2419 2
                        break;
2420 2 View Code Duplication
                    case 8:        // border color bottom
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...
2421 2
                        $xclfType = self::getInt2d($extData, 0); // color type
2422 2
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2423
2424 2
                        if ($xclfType == 2) {
2425 2
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2426
2427
                            // modify the relevant style property
2428 2
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2429 1
                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2430 1
                                $bottom->getColor()->setRGB($rgb);
2431 1
                                unset($bottom->colorIndex); // normal color index does not apply, discard
2432
                            }
2433
                        }
2434 2
                        break;
2435 2 View Code Duplication
                    case 9:        // border color left
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...
2436 2
                        $xclfType = self::getInt2d($extData, 0); // color type
2437 2
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2438
2439 2
                        if ($xclfType == 2) {
2440 2
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2441
2442
                            // modify the relevant style property
2443 2
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2444 1
                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2445 1
                                $left->getColor()->setRGB($rgb);
2446 1
                                unset($left->colorIndex); // normal color index does not apply, discard
2447
                            }
2448
                        }
2449 2
                        break;
2450 2 View Code Duplication
                    case 10:        // border color right
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...
2451 2
                        $xclfType = self::getInt2d($extData, 0); // color type
2452 2
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2453
2454 2
                        if ($xclfType == 2) {
2455 2
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2456
2457
                            // modify the relevant style property
2458 2
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2459 1
                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2460 1
                                $right->getColor()->setRGB($rgb);
2461 1
                                unset($right->colorIndex); // normal color index does not apply, discard
2462
                            }
2463
                        }
2464 2
                        break;
2465 2 View Code Duplication
                    case 11:        // border color diagonal
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...
2466
                        $xclfType = self::getInt2d($extData, 0); // color type
2467
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2468
2469
                        if ($xclfType == 2) {
2470
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2471
2472
                            // modify the relevant style property
2473
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2474
                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2475
                                $diagonal->getColor()->setRGB($rgb);
2476
                                unset($diagonal->colorIndex); // normal color index does not apply, discard
2477
                            }
2478
                        }
2479
                        break;
2480 2 View Code Duplication
                    case 13:    // font color
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...
2481 2
                        $xclfType = self::getInt2d($extData, 0); // color type
2482 2
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2483
2484 2
                        if ($xclfType == 2) {
2485 2
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2486
2487
                            // modify the relevant style property
2488 2
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2489 1
                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2490 1
                                $font->getColor()->setRGB($rgb);
2491 1
                                unset($font->colorIndex); // normal color index does not apply, discard
2492
                            }
2493
                        }
2494 2
                        break;
2495
                }
2496
2497 2
                $offset += $cb;
2498
            }
2499
        }
2500 2
    }
2501
2502
    /**
2503
     * Read STYLE record.
2504
     */
2505 4
    private function readStyle()
2506
    {
2507 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2508 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2509
2510
        // move stream pointer to next record
2511 4
        $this->pos += 4 + $length;
2512
2513 4
        if (!$this->readDataOnly) {
2514
            // offset: 0; size: 2; index to XF record and flag for built-in style
2515 4
            $ixfe = self::getInt2d($recordData, 0);
2516
2517
            // bit: 11-0; mask 0x0FFF; index to XF record
2518 4
            $xfIndex = (0x0FFF & $ixfe) >> 0;
0 ignored issues
show
Unused Code introduced by
$xfIndex 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...
2519
2520
            // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
2521 4
            $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
2522
2523 4
            if ($isBuiltIn) {
2524
                // offset: 2; size: 1; identifier for built-in style
2525 4
                $builtInId = ord($recordData[2]);
2526
2527
                switch ($builtInId) {
2528 4
                    case 0x00:
2529
                        // currently, we are not using this for anything
2530 4
                        break;
2531
                    default:
2532 2
                        break;
2533
                }
2534
            }
2535
                // user-defined; not supported by PhpSpreadsheet
2536
        }
2537 4
    }
2538
2539
    /**
2540
     * Read PALETTE record.
2541
     */
2542 3
    private function readPalette()
2543
    {
2544 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2545 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2546
2547
        // move stream pointer to next record
2548 3
        $this->pos += 4 + $length;
2549
2550 3
        if (!$this->readDataOnly) {
2551
            // offset: 0; size: 2; number of following colors
2552 3
            $nm = self::getInt2d($recordData, 0);
2553
2554
            // list of RGB colors
2555 3
            for ($i = 0; $i < $nm; ++$i) {
2556 3
                $rgb = substr($recordData, 2 + 4 * $i, 4);
2557 3
                $this->palette[] = self::readRGB($rgb);
2558
            }
2559
        }
2560 3
    }
2561
2562
    /**
2563
     * SHEET.
2564
     *
2565
     * This record is  located in the  Workbook Globals
2566
     * Substream  and represents a sheet inside the workbook.
2567
     * One SHEET record is written for each sheet. It stores the
2568
     * sheet name and a stream offset to the BOF record of the
2569
     * respective Sheet Substream within the Workbook Stream.
2570
     *
2571
     * --    "OpenOffice.org's Documentation of the Microsoft
2572
     *         Excel File Format"
2573
     */
2574 4
    private function readSheet()
2575
    {
2576 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2577 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2578
2579
        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
2580
        // NOTE: not encrypted
2581 4
        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
2582
2583
        // move stream pointer to next record
2584 4
        $this->pos += 4 + $length;
2585
2586
        // offset: 4; size: 1; sheet state
2587 4
        switch (ord($recordData[4])) {
2588 4
            case 0x00:
2589 4
                $sheetState = Worksheet::SHEETSTATE_VISIBLE;
2590 4
                break;
2591
            case 0x01:
2592
                $sheetState = Worksheet::SHEETSTATE_HIDDEN;
2593
                break;
2594
            case 0x02:
2595
                $sheetState = Worksheet::SHEETSTATE_VERYHIDDEN;
2596
                break;
2597
            default:
2598
                $sheetState = Worksheet::SHEETSTATE_VISIBLE;
2599
                break;
2600
        }
2601
2602
        // offset: 5; size: 1; sheet type
2603 4
        $sheetType = ord($recordData[5]);
2604
2605
        // offset: 6; size: var; sheet name
2606 4 View Code Duplication
        if ($this->version == self::XLS_BIFF8) {
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...
2607 4
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2608 4
            $rec_name = $string['value'];
2609
        } elseif ($this->version == self::XLS_BIFF7) {
2610
            $string = $this->readByteStringShort(substr($recordData, 6));
2611
            $rec_name = $string['value'];
2612
        }
2613
2614 4
        $this->sheets[] = [
2615 4
            'name' => $rec_name,
0 ignored issues
show
Bug introduced by
The variable $rec_name 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...
2616 4
            'offset' => $rec_offset,
2617 4
            'sheetState' => $sheetState,
2618 4
            'sheetType' => $sheetType,
2619
        ];
2620 4
    }
2621
2622
    /**
2623
     * Read EXTERNALBOOK record.
2624
     */
2625 3
    private function readExternalBook()
2626
    {
2627 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2628 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2629
2630
        // move stream pointer to next record
2631 3
        $this->pos += 4 + $length;
2632
2633
        // offset within record data
2634 3
        $offset = 0;
2635
2636
        // there are 4 types of records
2637 3
        if (strlen($recordData) > 4) {
2638
            // external reference
2639
            // offset: 0; size: 2; number of sheet names ($nm)
2640
            $nm = self::getInt2d($recordData, 0);
2641
            $offset += 2;
2642
2643
            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
2644
            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2645
            $offset += $encodedUrlString['size'];
2646
2647
            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
2648
            $externalSheetNames = [];
2649
            for ($i = 0; $i < $nm; ++$i) {
2650
                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2651
                $externalSheetNames[] = $externalSheetNameString['value'];
2652
                $offset += $externalSheetNameString['size'];
2653
            }
2654
2655
            // store the record data
2656
            $this->externalBooks[] = [
2657
                'type' => 'external',
2658
                'encodedUrl' => $encodedUrlString['value'],
2659
                'externalSheetNames' => $externalSheetNames,
2660
            ];
2661 3
        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
2662
            // internal reference
2663
            // offset: 0; size: 2; number of sheet in this document
2664
            // offset: 2; size: 2; 0x01 0x04
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
2665 3
            $this->externalBooks[] = [
2666 3
                'type' => 'internal',
2667
            ];
2668
        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
2669
            // add-in function
2670
            // offset: 0; size: 2; 0x0001
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
2671
            $this->externalBooks[] = [
2672
                'type' => 'addInFunction',
2673
            ];
2674
        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
2675
            // DDE links, OLE links
2676
            // offset: 0; size: 2; 0x0000
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
2677
            // offset: 2; size: var; encoded source document name
2678
            $this->externalBooks[] = [
2679
                'type' => 'DDEorOLE',
2680
            ];
2681
        }
2682 3
    }
2683
2684
    /**
2685
     * Read EXTERNNAME record.
2686
     */
2687
    private function readExternName()
2688
    {
2689
        $length = self::getInt2d($this->data, $this->pos + 2);
2690
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2691
2692
        // move stream pointer to next record
2693
        $this->pos += 4 + $length;
2694
2695
        // external sheet references provided for named cells
2696
        if ($this->version == self::XLS_BIFF8) {
2697
            // offset: 0; size: 2; options
2698
            $options = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$options 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...
2699
2700
            // offset: 2; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
2701
2702
            // offset: 4; size: 2; not used
2703
2704
            // offset: 6; size: var
2705
            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2706
2707
            // offset: var; size: var; formula data
2708
            $offset = 6 + $nameString['size'];
2709
            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
2710
2711
            $this->externalNames[] = [
2712
                'name' => $nameString['value'],
2713
                'formula' => $formula,
2714
            ];
2715
        }
2716
    }
2717
2718
    /**
2719
     * Read EXTERNSHEET record.
2720
     */
2721 3
    private function readExternSheet()
2722
    {
2723 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2724 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2725
2726
        // move stream pointer to next record
2727 3
        $this->pos += 4 + $length;
2728
2729
        // external sheet references provided for named cells
2730 3
        if ($this->version == self::XLS_BIFF8) {
2731
            // offset: 0; size: 2; number of following ref structures
2732 3
            $nm = self::getInt2d($recordData, 0);
2733 3
            for ($i = 0; $i < $nm; ++$i) {
2734 3
                $this->ref[] = [
2735
                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
2736 3
                    'externalBookIndex' => self::getInt2d($recordData, 2 + 6 * $i),
2737
                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
2738 3
                    'firstSheetIndex' => self::getInt2d($recordData, 4 + 6 * $i),
2739
                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
2740 3
                    'lastSheetIndex' => self::getInt2d($recordData, 6 + 6 * $i),
2741
                ];
2742
            }
2743
        }
2744 3
    }
2745
2746
    /**
2747
     * DEFINEDNAME.
2748
     *
2749
     * This record is part of a Link Table. It contains the name
2750
     * and the token array of an internal defined name. Token
2751
     * arrays of defined names contain tokens with aberrant
2752
     * token classes.
2753
     *
2754
     * --    "OpenOffice.org's Documentation of the Microsoft
2755
     *         Excel File Format"
2756
     */
2757 1
    private function readDefinedName()
2758
    {
2759 1
        $length = self::getInt2d($this->data, $this->pos + 2);
2760 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2761
2762
        // move stream pointer to next record
2763 1
        $this->pos += 4 + $length;
2764
2765 1
        if ($this->version == self::XLS_BIFF8) {
2766
            // retrieves named cells
2767
2768
            // offset: 0; size: 2; option flags
2769 1
            $opts = self::getInt2d($recordData, 0);
2770
2771
            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
2772 1
            $isBuiltInName = (0x0020 & $opts) >> 5;
2773
2774
            // offset: 2; size: 1; keyboard shortcut
2775
2776
            // offset: 3; size: 1; length of the name (character count)
2777 1
            $nlen = ord($recordData[3]);
2778
2779
            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
2780
            // note: there can also be additional data, this is not included in $flen
2781 1
            $flen = self::getInt2d($recordData, 4);
2782
2783
            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
2784 1
            $scope = self::getInt2d($recordData, 8);
2785
2786
            // offset: 14; size: var; Name (Unicode string without length field)
2787 1
            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2788
2789
            // offset: var; size: $flen; formula data
2790 1
            $offset = 14 + $string['size'];
2791 1
            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
2792
2793
            try {
2794 1
                $formula = $this->getFormulaFromStructure($formulaStructure);
2795
            } catch (PhpSpreadsheetException $e) {
2796
                $formula = '';
2797
            }
2798
2799 1
            $this->definedname[] = [
2800 1
                'isBuiltInName' => $isBuiltInName,
2801 1
                'name' => $string['value'],
2802 1
                'formula' => $formula,
2803 1
                'scope' => $scope,
2804
            ];
2805
        }
2806 1
    }
2807
2808
    /**
2809
     * Read MSODRAWINGGROUP record.
2810
     */
2811 3 View Code Duplication
    private function readMsoDrawingGroup()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2812
    {
2813 3
        $length = self::getInt2d($this->data, $this->pos + 2);
0 ignored issues
show
Unused Code introduced by
$length 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...
2814
2815
        // get spliced record data
2816 3
        $splicedRecordData = $this->getSplicedRecordData();
2817 3
        $recordData = $splicedRecordData['recordData'];
2818
2819 3
        $this->drawingGroupData .= $recordData;
2820 3
    }
2821
2822
    /**
2823
     * SST - Shared String Table.
2824
     *
2825
     * This record contains a list of all strings used anywhere
2826
     * in the workbook. Each string occurs only once. The
2827
     * workbook uses indexes into the list to reference the
2828
     * strings.
2829
     *
2830
     * --    "OpenOffice.org's Documentation of the Microsoft
2831
     *         Excel File Format"
2832
     **/
2833 4
    private function readSst()
2834
    {
2835
        // offset within (spliced) record data
2836 4
        $pos = 0;
2837
2838
        // get spliced record data
2839 4
        $splicedRecordData = $this->getSplicedRecordData();
2840
2841 4
        $recordData = $splicedRecordData['recordData'];
2842 4
        $spliceOffsets = $splicedRecordData['spliceOffsets'];
2843
2844
        // offset: 0; size: 4; total number of strings in the workbook
2845 4
        $pos += 4;
2846
2847
        // offset: 4; size: 4; number of following strings ($nm)
2848 4
        $nm = self::getInt4d($recordData, 4);
2849 4
        $pos += 4;
2850
2851
        // loop through the Unicode strings (16-bit length)
2852 4
        for ($i = 0; $i < $nm; ++$i) {
2853
            // number of characters in the Unicode string
2854 4
            $numChars = self::getInt2d($recordData, $pos);
2855 4
            $pos += 2;
2856
2857
            // option flags
2858 4
            $optionFlags = ord($recordData[$pos]);
2859 4
            ++$pos;
2860
2861
            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
2862 4
            $isCompressed = (($optionFlags & 0x01) == 0);
2863
2864
            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
2865 4
            $hasAsian = (($optionFlags & 0x04) != 0);
2866
2867
            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
2868 4
            $hasRichText = (($optionFlags & 0x08) != 0);
2869
2870 4
            if ($hasRichText) {
2871
                // number of Rich-Text formatting runs
2872 2
                $formattingRuns = self::getInt2d($recordData, $pos);
2873 2
                $pos += 2;
2874
            }
2875
2876 4
            if ($hasAsian) {
2877
                // size of Asian phonetic setting
2878
                $extendedRunLength = self::getInt4d($recordData, $pos);
2879
                $pos += 4;
2880
            }
2881
2882
            // expected byte length of character array if not split
2883 4
            $len = ($isCompressed) ? $numChars : $numChars * 2;
2884
2885
            // look up limit position
2886 4
            foreach ($spliceOffsets as $spliceOffset) {
2887
                // it can happen that the string is empty, therefore we need
2888
                // <= and not just <
2889 4
                if ($pos <= $spliceOffset) {
2890 4
                    $limitpos = $spliceOffset;
2891 4
                    break;
2892
                }
2893
            }
2894
2895 4
            if ($pos + $len <= $limitpos) {
2896
                // character array is not split between records
2897
2898 4
                $retstr = substr($recordData, $pos, $len);
2899 4
                $pos += $len;
2900
            } else {
2901
                // character array is split between records
2902
2903
                // first part of character array
2904
                $retstr = substr($recordData, $pos, $limitpos - $pos);
0 ignored issues
show
Bug introduced by
The variable $limitpos 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...
2905
2906
                $bytesRead = $limitpos - $pos;
2907
2908
                // remaining characters in Unicode string
2909
                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2910
2911
                $pos = $limitpos;
2912
2913
                // keep reading the characters
2914
                while ($charsLeft > 0) {
2915
                    // look up next limit position, in case the string span more than one continue record
2916
                    foreach ($spliceOffsets as $spliceOffset) {
2917
                        if ($pos < $spliceOffset) {
2918
                            $limitpos = $spliceOffset;
2919
                            break;
2920
                        }
2921
                    }
2922
2923
                    // repeated option flags
2924
                    // OpenOffice.org documentation 5.21
2925
                    $option = ord($recordData[$pos]);
2926
                    ++$pos;
2927
2928
                    if ($isCompressed && ($option == 0)) {
2929
                        // 1st fragment compressed
2930
                        // this fragment compressed
2931
                        $len = min($charsLeft, $limitpos - $pos);
2932
                        $retstr .= substr($recordData, $pos, $len);
2933
                        $charsLeft -= $len;
2934
                        $isCompressed = true;
2935
                    } elseif (!$isCompressed && ($option != 0)) {
2936
                        // 1st fragment uncompressed
2937
                        // this fragment uncompressed
2938
                        $len = min($charsLeft * 2, $limitpos - $pos);
2939
                        $retstr .= substr($recordData, $pos, $len);
2940
                        $charsLeft -= $len / 2;
2941
                        $isCompressed = false;
2942
                    } elseif (!$isCompressed && ($option == 0)) {
2943
                        // 1st fragment uncompressed
2944
                        // this fragment compressed
2945
                        $len = min($charsLeft, $limitpos - $pos);
2946
                        for ($j = 0; $j < $len; ++$j) {
2947
                            $retstr .= $recordData[$pos + $j]
2948
                            . chr(0);
2949
                        }
2950
                        $charsLeft -= $len;
2951
                        $isCompressed = false;
2952
                    } else {
2953
                        // 1st fragment compressed
2954
                        // this fragment uncompressed
2955
                        $newstr = '';
2956
                        for ($j = 0; $j < strlen($retstr); ++$j) {
2957
                            $newstr .= $retstr[$j] . chr(0);
2958
                        }
2959
                        $retstr = $newstr;
2960
                        $len = min($charsLeft * 2, $limitpos - $pos);
2961
                        $retstr .= substr($recordData, $pos, $len);
2962
                        $charsLeft -= $len / 2;
2963
                        $isCompressed = false;
2964
                    }
2965
2966
                    $pos += $len;
2967
                }
2968
            }
2969
2970
            // convert to UTF-8
2971 4
            $retstr = self::encodeUTF16($retstr, $isCompressed);
2972
2973
            // read additional Rich-Text information, if any
2974 4
            $fmtRuns = [];
2975 4
            if ($hasRichText) {
2976
                // list of formatting runs
2977 2
                for ($j = 0; $j < $formattingRuns; ++$j) {
0 ignored issues
show
Bug introduced by
The variable $formattingRuns 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...
2978
                    // first formatted character; zero-based
2979 2
                    $charPos = self::getInt2d($recordData, $pos + $j * 4);
2980
2981
                    // index to font record
2982 2
                    $fontIndex = self::getInt2d($recordData, $pos + 2 + $j * 4);
2983
2984 2
                    $fmtRuns[] = [
2985 2
                        'charPos' => $charPos,
2986 2
                        'fontIndex' => $fontIndex,
2987
                    ];
2988
                }
2989 2
                $pos += 4 * $formattingRuns;
2990
            }
2991
2992
            // read additional Asian phonetics information, if any
2993 4
            if ($hasAsian) {
2994
                // For Asian phonetic settings, we skip the extended string data
2995
                $pos += $extendedRunLength;
0 ignored issues
show
Bug introduced by
The variable $extendedRunLength 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...
2996
            }
2997
2998
            // store the shared sting
2999 4
            $this->sst[] = [
3000 4
                'value' => $retstr,
3001 4
                'fmtRuns' => $fmtRuns,
3002
            ];
3003
        }
3004
3005
        // getSplicedRecordData() takes care of moving current position in data stream
3006 4
    }
3007
3008
    /**
3009
     * Read PRINTGRIDLINES record.
3010
     */
3011 4
    private function readPrintGridlines()
3012
    {
3013 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3014 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3015
3016
        // move stream pointer to next record
3017 4
        $this->pos += 4 + $length;
3018
3019 4
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3020
            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
3021 4
            $printGridlines = (bool) self::getInt2d($recordData, 0);
3022 4
            $this->phpSheet->setPrintGridlines($printGridlines);
3023
        }
3024 4
    }
3025
3026
    /**
3027
     * Read DEFAULTROWHEIGHT record.
3028
     */
3029 3 View Code Duplication
    private function readDefaultRowHeight()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3030
    {
3031 3
        $length = self::getInt2d($this->data, $this->pos + 2);
3032 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3033
3034
        // move stream pointer to next record
3035 3
        $this->pos += 4 + $length;
3036
3037
        // offset: 0; size: 2; option flags
3038
        // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
3039 3
        $height = self::getInt2d($recordData, 2);
3040 3
        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3041 3
    }
3042
3043
    /**
3044
     * Read SHEETPR record.
3045
     */
3046 4
    private function readSheetPr()
3047
    {
3048 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3049 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3050
3051
        // move stream pointer to next record
3052 4
        $this->pos += 4 + $length;
3053
3054
        // offset: 0; size: 2
3055
3056
        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
3057 4
        $isSummaryBelow = (0x0040 & self::getInt2d($recordData, 0)) >> 6;
3058 4
        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
0 ignored issues
show
Documentation introduced by
$isSummaryBelow is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3059
3060
        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
3061 4
        $isSummaryRight = (0x0080 & self::getInt2d($recordData, 0)) >> 7;
3062 4
        $this->phpSheet->setShowSummaryRight($isSummaryRight);
0 ignored issues
show
Documentation introduced by
$isSummaryRight is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3063
3064
        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
3065
        // this corresponds to radio button setting in page setup dialog in Excel
3066 4
        $this->isFitToPages = (bool) ((0x0100 & self::getInt2d($recordData, 0)) >> 8);
3067 4
    }
3068
3069
    /**
3070
     * Read HORIZONTALPAGEBREAKS record.
3071
     */
3072 View Code Duplication
    private function readHorizontalPageBreaks()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3073
    {
3074
        $length = self::getInt2d($this->data, $this->pos + 2);
3075
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3076
3077
        // move stream pointer to next record
3078
        $this->pos += 4 + $length;
3079
3080
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3081
            // offset: 0; size: 2; number of the following row index structures
3082
            $nm = self::getInt2d($recordData, 0);
3083
3084
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3085
            for ($i = 0; $i < $nm; ++$i) {
3086
                $r = self::getInt2d($recordData, 2 + 6 * $i);
3087
                $cf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3088
                $cl = self::getInt2d($recordData, 2 + 6 * $i + 4);
0 ignored issues
show
Unused Code introduced by
$cl 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...
3089
3090
                // not sure why two column indexes are necessary?
3091
                $this->phpSheet->setBreakByColumnAndRow($cf, $r, Worksheet::BREAK_ROW);
3092
            }
3093
        }
3094
    }
3095
3096
    /**
3097
     * Read VERTICALPAGEBREAKS record.
3098
     */
3099 View Code Duplication
    private function readVerticalPageBreaks()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3100
    {
3101
        $length = self::getInt2d($this->data, $this->pos + 2);
3102
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3103
3104
        // move stream pointer to next record
3105
        $this->pos += 4 + $length;
3106
3107
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3108
            // offset: 0; size: 2; number of the following column index structures
3109
            $nm = self::getInt2d($recordData, 0);
3110
3111
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3112
            for ($i = 0; $i < $nm; ++$i) {
3113
                $c = self::getInt2d($recordData, 2 + 6 * $i);
3114
                $rf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3115
                $rl = self::getInt2d($recordData, 2 + 6 * $i + 4);
0 ignored issues
show
Unused Code introduced by
$rl 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...
3116
3117
                // not sure why two row indexes are necessary?
3118
                $this->phpSheet->setBreakByColumnAndRow($c, $rf, Worksheet::BREAK_COLUMN);
3119
            }
3120
        }
3121
    }
3122
3123
    /**
3124
     * Read HEADER record.
3125
     */
3126 4 View Code Duplication
    private function readHeader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3127
    {
3128 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3129 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3130
3131
        // move stream pointer to next record
3132 4
        $this->pos += 4 + $length;
3133
3134 4
        if (!$this->readDataOnly) {
3135
            // offset: 0; size: var
3136
            // realized that $recordData can be empty even when record exists
3137 4
            if ($recordData) {
3138 3
                if ($this->version == self::XLS_BIFF8) {
3139 3
                    $string = self::readUnicodeStringLong($recordData);
3140
                } else {
3141
                    $string = $this->readByteStringShort($recordData);
3142
                }
3143
3144 3
                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
3145 3
                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
3146
            }
3147
        }
3148 4
    }
3149
3150
    /**
3151
     * Read FOOTER record.
3152
     */
3153 4 View Code Duplication
    private function readFooter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3154
    {
3155 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3156 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3157
3158
        // move stream pointer to next record
3159 4
        $this->pos += 4 + $length;
3160
3161 4
        if (!$this->readDataOnly) {
3162
            // offset: 0; size: var
3163
            // realized that $recordData can be empty even when record exists
3164 4
            if ($recordData) {
3165 3
                if ($this->version == self::XLS_BIFF8) {
3166 3
                    $string = self::readUnicodeStringLong($recordData);
3167
                } else {
3168
                    $string = $this->readByteStringShort($recordData);
3169
                }
3170 3
                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
3171 3
                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
3172
            }
3173
        }
3174 4
    }
3175
3176
    /**
3177
     * Read HCENTER record.
3178
     */
3179 4 View Code Duplication
    private function readHcenter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3180
    {
3181 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3182 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3183
3184
        // move stream pointer to next record
3185 4
        $this->pos += 4 + $length;
3186
3187 4
        if (!$this->readDataOnly) {
3188
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3189 4
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3190
3191 4
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3192
        }
3193 4
    }
3194
3195
    /**
3196
     * Read VCENTER record.
3197
     */
3198 4 View Code Duplication
    private function readVcenter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3199
    {
3200 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3201 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3202
3203
        // move stream pointer to next record
3204 4
        $this->pos += 4 + $length;
3205
3206 4
        if (!$this->readDataOnly) {
3207
            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
3208 4
            $isVerticalCentered = (bool) self::getInt2d($recordData, 0);
3209
3210 4
            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3211
        }
3212 4
    }
3213
3214
    /**
3215
     * Read LEFTMARGIN record.
3216
     */
3217 4
    private function readLeftMargin()
3218
    {
3219 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3220 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3221
3222
        // move stream pointer to next record
3223 4
        $this->pos += 4 + $length;
3224
3225 4
        if (!$this->readDataOnly) {
3226
            // offset: 0; size: 8
3227 4
            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3228
        }
3229 4
    }
3230
3231
    /**
3232
     * Read RIGHTMARGIN record.
3233
     */
3234 4
    private function readRightMargin()
3235
    {
3236 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3237 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3238
3239
        // move stream pointer to next record
3240 4
        $this->pos += 4 + $length;
3241
3242 4
        if (!$this->readDataOnly) {
3243
            // offset: 0; size: 8
3244 4
            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3245
        }
3246 4
    }
3247
3248
    /**
3249
     * Read TOPMARGIN record.
3250
     */
3251 4
    private function readTopMargin()
3252
    {
3253 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3254 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3255
3256
        // move stream pointer to next record
3257 4
        $this->pos += 4 + $length;
3258
3259 4
        if (!$this->readDataOnly) {
3260
            // offset: 0; size: 8
3261 4
            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3262
        }
3263 4
    }
3264
3265
    /**
3266
     * Read BOTTOMMARGIN record.
3267
     */
3268 4
    private function readBottomMargin()
3269
    {
3270 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3271 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3272
3273
        // move stream pointer to next record
3274 4
        $this->pos += 4 + $length;
3275
3276 4
        if (!$this->readDataOnly) {
3277
            // offset: 0; size: 8
3278 4
            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3279
        }
3280 4
    }
3281
3282
    /**
3283
     * Read PAGESETUP record.
3284
     */
3285 4
    private function readPageSetup()
3286
    {
3287 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3288 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3289
3290
        // move stream pointer to next record
3291 4
        $this->pos += 4 + $length;
3292
3293 4
        if (!$this->readDataOnly) {
3294
            // offset: 0; size: 2; paper size
3295 4
            $paperSize = self::getInt2d($recordData, 0);
3296
3297
            // offset: 2; size: 2; scaling factor
3298 4
            $scale = self::getInt2d($recordData, 2);
3299
3300
            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
3301 4
            $fitToWidth = self::getInt2d($recordData, 6);
3302
3303
            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
3304 4
            $fitToHeight = self::getInt2d($recordData, 8);
3305
3306
            // offset: 10; size: 2; option flags
3307
3308
            // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
3309 4
            $isPortrait = (0x0002 & self::getInt2d($recordData, 10)) >> 1;
3310
3311
            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
3312
            // when this bit is set, do not use flags for those properties
3313 4
            $isNotInit = (0x0004 & self::getInt2d($recordData, 10)) >> 2;
3314
3315 4
            if (!$isNotInit) {
3316 4
                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3317
                switch ($isPortrait) {
3318 4
                    case 0:
3319 2
                        $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
3320 2
                        break;
3321 4
                    case 1:
3322 4
                        $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_PORTRAIT);
3323 4
                        break;
3324
                }
3325
3326 4
                $this->phpSheet->getPageSetup()->setScale($scale, false);
3327 4
                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
3328 4
                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
3329 4
                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
3330
            }
3331
3332
            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
3333 4
            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3334 4
            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3335
3336
            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
3337 4
            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3338 4
            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3339
        }
3340 4
    }
3341
3342
    /**
3343
     * PROTECT - Sheet protection (BIFF2 through BIFF8)
3344
     *   if this record is omitted, then it also means no sheet protection.
3345
     */
3346 1 View Code Duplication
    private function readProtect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3347
    {
3348 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3349 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3350
3351
        // move stream pointer to next record
3352 1
        $this->pos += 4 + $length;
3353
3354 1
        if ($this->readDataOnly) {
3355
            return;
3356
        }
3357
3358
        // offset: 0; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
3359
3360
        // bit 0, mask 0x01; 1 = sheet is protected
3361 1
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3362 1
        $this->phpSheet->getProtection()->setSheet((bool) $bool);
3363 1
    }
3364
3365
    /**
3366
     * SCENPROTECT.
3367
     */
3368 View Code Duplication
    private function readScenProtect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3369
    {
3370
        $length = self::getInt2d($this->data, $this->pos + 2);
3371
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3372
3373
        // move stream pointer to next record
3374
        $this->pos += 4 + $length;
3375
3376
        if ($this->readDataOnly) {
3377
            return;
3378
        }
3379
3380
        // offset: 0; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
3381
3382
        // bit: 0, mask 0x01; 1 = scenarios are protected
3383
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3384
3385
        $this->phpSheet->getProtection()->setScenarios((bool) $bool);
3386
    }
3387
3388
    /**
3389
     * OBJECTPROTECT.
3390
     */
3391 View Code Duplication
    private function readObjectProtect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3392
    {
3393
        $length = self::getInt2d($this->data, $this->pos + 2);
3394
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3395
3396
        // move stream pointer to next record
3397
        $this->pos += 4 + $length;
3398
3399
        if ($this->readDataOnly) {
3400
            return;
3401
        }
3402
3403
        // offset: 0; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
3404
3405
        // bit: 0, mask 0x01; 1 = objects are protected
3406
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3407
3408
        $this->phpSheet->getProtection()->setObjects((bool) $bool);
3409
    }
3410
3411
    /**
3412
     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
3413
     */
3414
    private function readPassword()
3415
    {
3416
        $length = self::getInt2d($this->data, $this->pos + 2);
3417
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3418
3419
        // move stream pointer to next record
3420
        $this->pos += 4 + $length;
3421
3422
        if (!$this->readDataOnly) {
3423
            // offset: 0; size: 2; 16-bit hash value of password
3424
            $password = strtoupper(dechex(self::getInt2d($recordData, 0))); // the hashed password
3425
            $this->phpSheet->getProtection()->setPassword($password, true);
3426
        }
3427
    }
3428
3429
    /**
3430
     * Read DEFCOLWIDTH record.
3431
     */
3432 4 View Code Duplication
    private function readDefColWidth()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3433
    {
3434 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3435 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3436
3437
        // move stream pointer to next record
3438 4
        $this->pos += 4 + $length;
3439
3440
        // offset: 0; size: 2; default column width
3441 4
        $width = self::getInt2d($recordData, 0);
3442 4
        if ($width != 8) {
3443 1
            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3444
        }
3445 4
    }
3446
3447
    /**
3448
     * Read COLINFO record.
3449
     */
3450 4
    private function readColInfo()
3451
    {
3452 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3453 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3454
3455
        // move stream pointer to next record
3456 4
        $this->pos += 4 + $length;
3457
3458 4
        if (!$this->readDataOnly) {
3459
            // offset: 0; size: 2; index to first column in range
3460 4
            $fc = self::getInt2d($recordData, 0); // first column index
3461
3462
            // offset: 2; size: 2; index to last column in range
3463 4
            $lc = self::getInt2d($recordData, 2); // first column index
3464
3465
            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
3466 4
            $width = self::getInt2d($recordData, 4);
3467
3468
            // offset: 6; size: 2; index to XF record for default column formatting
3469 4
            $xfIndex = self::getInt2d($recordData, 6);
3470
3471
            // offset: 8; size: 2; option flags
3472
            // bit: 0; mask: 0x0001; 1= columns are hidden
3473 4
            $isHidden = (0x0001 & self::getInt2d($recordData, 8)) >> 0;
3474
3475
            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
3476 4
            $level = (0x0700 & self::getInt2d($recordData, 8)) >> 8;
3477
3478
            // bit: 12; mask: 0x1000; 1 = collapsed
3479 4
            $isCollapsed = (0x1000 & self::getInt2d($recordData, 8)) >> 12;
3480
3481
            // offset: 10; size: 2; not used
3482
3483 4
            for ($i = $fc; $i <= $lc; ++$i) {
3484 4
                if ($lc == 255 || $lc == 256) {
3485 1
                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3486 1
                    break;
3487
                }
3488 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
3489 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
3490 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
3491 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
0 ignored issues
show
Documentation introduced by
$isCollapsed is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3492 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3493
            }
3494
        }
3495 4
    }
3496
3497
    /**
3498
     * ROW.
3499
     *
3500
     * This record contains the properties of a single row in a
3501
     * sheet. Rows and cells in a sheet are divided into blocks
3502
     * of 32 rows.
3503
     *
3504
     * --    "OpenOffice.org's Documentation of the Microsoft
3505
     *         Excel File Format"
3506
     */
3507 3
    private function readRow()
3508
    {
3509 3
        $length = self::getInt2d($this->data, $this->pos + 2);
3510 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3511
3512
        // move stream pointer to next record
3513 3
        $this->pos += 4 + $length;
3514
3515 3
        if (!$this->readDataOnly) {
3516
            // offset: 0; size: 2; index of this row
3517 3
            $r = self::getInt2d($recordData, 0);
3518
3519
            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
3520
3521
            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
3522
3523
            // offset: 6; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
3524
3525
            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
3526 3
            $height = (0x7FFF & self::getInt2d($recordData, 6)) >> 0;
3527
3528
            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
3529 3
            $useDefaultHeight = (0x8000 & self::getInt2d($recordData, 6)) >> 15;
3530
3531 3
            if (!$useDefaultHeight) {
3532 3
                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
3533
            }
3534
3535
            // offset: 8; size: 2; not used
3536
3537
            // offset: 10; size: 2; not used in BIFF5-BIFF8
3538
3539
            // offset: 12; size: 4; option flags and default row formatting
3540
3541
            // bit: 2-0: mask: 0x00000007; outline level of the row
3542 3
            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3543 3
            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
3544
3545
            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
3546 3
            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
3547 3
            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
0 ignored issues
show
Documentation introduced by
$isCollapsed is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3548
3549
            // bit: 5; mask: 0x00000020; 1 = row is hidden
3550 3
            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3551 3
            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
3552
3553
            // bit: 7; mask: 0x00000080; 1 = row has explicit format
3554 3
            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3555
3556
            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
3557 3
            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3558
3559 3
            if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
3560
                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3561
            }
3562
        }
3563 3
    }
3564
3565
    /**
3566
     * Read RK record
3567
     * This record represents a cell that contains an RK value
3568
     * (encoded integer or floating-point value). If a
3569
     * floating-point value cannot be encoded to an RK value,
3570
     * a NUMBER record will be written. This record replaces the
3571
     * record INTEGER written in BIFF2.
3572
     *
3573
     * --    "OpenOffice.org's Documentation of the Microsoft
3574
     *         Excel File Format"
3575
     */
3576 1 View Code Duplication
    private function readRk()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3577
    {
3578 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3579 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3580
3581
        // move stream pointer to next record
3582 1
        $this->pos += 4 + $length;
3583
3584
        // offset: 0; size: 2; index to row
3585 1
        $row = self::getInt2d($recordData, 0);
3586
3587
        // offset: 2; size: 2; index to column
3588 1
        $column = self::getInt2d($recordData, 2);
3589 1
        $columnString = Cell::stringFromColumnIndex($column);
3590
3591
        // Read cell?
3592 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3593
            // offset: 4; size: 2; index to XF record
3594 1
            $xfIndex = self::getInt2d($recordData, 4);
3595
3596
            // offset: 6; size: 4; RK value
3597 1
            $rknum = self::getInt4d($recordData, 6);
3598 1
            $numValue = self::getIEEE754($rknum);
3599
3600 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3601 1
            if (!$this->readDataOnly) {
3602
                // add style information
3603 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3604
            }
3605
3606
            // add cell
3607 1
            $cell->setValueExplicit($numValue, Cell\DataType::TYPE_NUMERIC);
3608
        }
3609 1
    }
3610
3611
    /**
3612
     * Read LABELSST record
3613
     * This record represents a cell that contains a string. It
3614
     * replaces the LABEL record and RSTRING record used in
3615
     * BIFF2-BIFF5.
3616
     *
3617
     * --    "OpenOffice.org's Documentation of the Microsoft
3618
     *         Excel File Format"
3619
     */
3620 4
    private function readLabelSst()
3621
    {
3622 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3623 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3624
3625
        // move stream pointer to next record
3626 4
        $this->pos += 4 + $length;
3627
3628
        // offset: 0; size: 2; index to row
3629 4
        $row = self::getInt2d($recordData, 0);
3630
3631
        // offset: 2; size: 2; index to column
3632 4
        $column = self::getInt2d($recordData, 2);
3633 4
        $columnString = Cell::stringFromColumnIndex($column);
3634
3635 4
        $emptyCell = true;
3636
        // Read cell?
3637 4
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3638
            // offset: 4; size: 2; index to XF record
3639 4
            $xfIndex = self::getInt2d($recordData, 4);
3640
3641
            // offset: 6; size: 4; index to SST record
3642 4
            $index = self::getInt4d($recordData, 6);
3643
3644
            // add cell
3645 4
            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
3646
                // then we should treat as rich text
3647 2
                $richText = new RichText();
3648 2
                $charPos = 0;
3649 2
                $sstCount = count($this->sst[$index]['fmtRuns']);
3650 2
                for ($i = 0; $i <= $sstCount; ++$i) {
3651 2
                    if (isset($fmtRuns[$i])) {
3652 2
                        $text = StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
3653 2
                        $charPos = $fmtRuns[$i]['charPos'];
3654
                    } else {
3655 2
                        $text = StringHelper::substring($this->sst[$index]['value'], $charPos, StringHelper::countCharacters($this->sst[$index]['value']));
3656
                    }
3657
3658 2
                    if (StringHelper::countCharacters($text) > 0) {
3659 2
                        if ($i == 0) { // first text run, no style
3660 1
                            $richText->createText($text);
3661
                        } else {
3662 2
                            $textRun = $richText->createTextRun($text);
3663 2
                            if (isset($fmtRuns[$i - 1])) {
3664 2
                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
3665 2
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
3666
                                } else {
3667
                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
3668
                                    // check the OpenOffice documentation of the FONT record
3669 2
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
3670
                                }
3671 2
                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
3672
                            }
3673
                        }
3674
                    }
3675
                }
3676 2
                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
3677 2
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3678 2
                    $cell->setValueExplicit($richText, Cell\DataType::TYPE_STRING);
3679 2
                    $emptyCell = false;
3680
                }
3681
            } else {
3682 4
                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
3683 4
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3684 4
                    $cell->setValueExplicit($this->sst[$index]['value'], Cell\DataType::TYPE_STRING);
3685 4
                    $emptyCell = false;
3686
                }
3687
            }
3688
3689 4
            if (!$this->readDataOnly && !$emptyCell) {
3690
                // add style information
3691 4
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
0 ignored issues
show
Bug introduced by
The variable $cell 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...
3692
            }
3693
        }
3694 4
    }
3695
3696
    /**
3697
     * Read MULRK record
3698
     * This record represents a cell range containing RK value
3699
     * cells. All cells are located in the same row.
3700
     *
3701
     * --    "OpenOffice.org's Documentation of the Microsoft
3702
     *         Excel File Format"
3703
     */
3704
    private function readMulRk()
3705
    {
3706
        $length = self::getInt2d($this->data, $this->pos + 2);
3707
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3708
3709
        // move stream pointer to next record
3710
        $this->pos += 4 + $length;
3711
3712
        // offset: 0; size: 2; index to row
3713
        $row = self::getInt2d($recordData, 0);
3714
3715
        // offset: 2; size: 2; index to first column
3716
        $colFirst = self::getInt2d($recordData, 2);
3717
3718
        // offset: var; size: 2; index to last column
3719
        $colLast = self::getInt2d($recordData, $length - 2);
3720
        $columns = $colLast - $colFirst + 1;
3721
3722
        // offset within record data
3723
        $offset = 4;
3724
3725
        for ($i = 0; $i < $columns; ++$i) {
3726
            $columnString = Cell::stringFromColumnIndex($colFirst + $i);
3727
3728
            // Read cell?
3729
            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3730
                // offset: var; size: 2; index to XF record
3731
                $xfIndex = self::getInt2d($recordData, $offset);
3732
3733
                // offset: var; size: 4; RK value
3734
                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3735
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3736
                if (!$this->readDataOnly) {
3737
                    // add style
3738
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3739
                }
3740
3741
                // add cell value
3742
                $cell->setValueExplicit($numValue, Cell\DataType::TYPE_NUMERIC);
3743
            }
3744
3745
            $offset += 6;
3746
        }
3747
    }
3748
3749
    /**
3750
     * Read NUMBER record
3751
     * This record represents a cell that contains a
3752
     * floating-point value.
3753
     *
3754
     * --    "OpenOffice.org's Documentation of the Microsoft
3755
     *         Excel File Format"
3756
     */
3757 1 View Code Duplication
    private function readNumber()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
3758
    {
3759 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3760 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3761
3762
        // move stream pointer to next record
3763 1
        $this->pos += 4 + $length;
3764
3765
        // offset: 0; size: 2; index to row
3766 1
        $row = self::getInt2d($recordData, 0);
3767
3768
        // offset: 2; size 2; index to column
3769 1
        $column = self::getInt2d($recordData, 2);
3770 1
        $columnString = Cell::stringFromColumnIndex($column);
3771
3772
        // Read cell?
3773 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3774
            // offset 4; size: 2; index to XF record
3775 1
            $xfIndex = self::getInt2d($recordData, 4);
3776
3777 1
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3778
3779 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3780 1
            if (!$this->readDataOnly) {
3781
                // add cell style
3782 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3783
            }
3784
3785
            // add cell value
3786 1
            $cell->setValueExplicit($numValue, Cell\DataType::TYPE_NUMERIC);
3787
        }
3788 1
    }
3789
3790
    /**
3791
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3792
     * This record contains the token array and the result of a
3793
     * formula cell.
3794
     *
3795
     * --    "OpenOffice.org's Documentation of the Microsoft
3796
     *         Excel File Format"
3797
     */
3798 2
    private function readFormula()
3799
    {
3800 2
        $length = self::getInt2d($this->data, $this->pos + 2);
3801 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3802
3803
        // move stream pointer to next record
3804 2
        $this->pos += 4 + $length;
3805
3806
        // offset: 0; size: 2; row index
3807 2
        $row = self::getInt2d($recordData, 0);
3808
3809
        // offset: 2; size: 2; col index
3810 2
        $column = self::getInt2d($recordData, 2);
3811 2
        $columnString = Cell::stringFromColumnIndex($column);
3812
3813
        // offset: 20: size: variable; formula structure
3814 2
        $formulaStructure = substr($recordData, 20);
3815
3816
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3817 2
        $options = self::getInt2d($recordData, 14);
3818
3819
        // bit: 0; mask: 0x0001; 1 = recalculate always
3820
        // bit: 1; mask: 0x0002; 1 = calculate on open
3821
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3822 2
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3823
3824
        // WARNING:
3825
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3826
        // the formula data may be ordinary formula data, therefore we need to check
3827
        // explicitly for the tExp token (0x01)
3828 2
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
3829
3830 2
        if ($isPartOfSharedFormula) {
3831
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3832
            // get the base cell, grab tExp token
3833
            $baseRow = self::getInt2d($formulaStructure, 3);
3834
            $baseCol = self::getInt2d($formulaStructure, 5);
3835
            $this->_baseCell = Cell::stringFromColumnIndex($baseCol) . ($baseRow + 1);
0 ignored issues
show
Bug introduced by
The property _baseCell does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
3836
        }
3837
3838
        // Read cell?
3839 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3840 2
            if ($isPartOfSharedFormula) {
3841
                // formula is added to this cell after the sheet has been read
3842
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3843
            }
3844
3845
            // offset: 16: size: 4; not used
3846
3847
            // offset: 4; size: 2; XF index
3848 2
            $xfIndex = self::getInt2d($recordData, 4);
3849
3850
            // offset: 6; size: 8; result of the formula
3851 2
            if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
3852
                // String formula. Result follows in appended STRING record
3853
                $dataType = Cell\DataType::TYPE_STRING;
3854
3855
                // read possible SHAREDFMLA record
3856
                $code = self::getInt2d($this->data, $this->pos);
3857
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3858
                    $this->readSharedFmla();
3859
                }
3860
3861
                // read STRING record
3862
                $value = $this->readString();
3863 2 View Code Duplication
            } elseif ((ord($recordData[6]) == 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...
3864 2
                && (ord($recordData[12]) == 255)
3865 2
                && (ord($recordData[13]) == 255)) {
3866
                // Boolean formula. Result is in +2; 0=false, 1=true
3867
                $dataType = Cell\DataType::TYPE_BOOL;
3868
                $value = (bool) ord($recordData[8]);
3869 2
            } elseif ((ord($recordData[6]) == 2)
3870 2
                && (ord($recordData[12]) == 255)
3871 2
                && (ord($recordData[13]) == 255)) {
3872
                // Error formula. Error code is in +2
3873
                $dataType = Cell\DataType::TYPE_ERROR;
3874
                $value = Xls\ErrorCode::lookup(ord($recordData[8]));
3875 2 View Code Duplication
            } elseif ((ord($recordData[6]) == 3)
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...
3876 2
                && (ord($recordData[12]) == 255)
3877 2
                && (ord($recordData[13]) == 255)) {
3878
                // Formula result is a null string
3879 1
                $dataType = Cell\DataType::TYPE_NULL;
3880 1
                $value = '';
3881
            } else {
3882
                // forumla result is a number, first 14 bytes like _NUMBER record
3883 2
                $dataType = Cell\DataType::TYPE_NUMERIC;
3884 2
                $value = self::extractNumber(substr($recordData, 6, 8));
3885
            }
3886
3887 2
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3888 2
            if (!$this->readDataOnly) {
3889
                // add cell style
3890 2
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3891
            }
3892
3893
            // store the formula
3894 2
            if (!$isPartOfSharedFormula) {
3895
                // not part of shared formula
3896
                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
3897
                try {
3898 2
                    if ($this->version != self::XLS_BIFF8) {
3899
                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
3900
                    }
3901 2
                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
3902 2
                    $cell->setValueExplicit('=' . $formula, Cell\DataType::TYPE_FORMULA);
3903
                } catch (PhpSpreadsheetException $e) {
3904 2
                    $cell->setValueExplicit($value, $dataType);
3905
                }
3906
            } else {
3907
                if ($this->version == self::XLS_BIFF8) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3908
                    // do nothing at this point, formula id added later in the code
3909
                } else {
3910
                    $cell->setValueExplicit($value, $dataType);
3911
                }
3912
            }
3913
3914
            // store the cached calculated value
3915 2
            $cell->setCalculatedValue($value);
3916
        }
3917 2
    }
3918
3919
    /**
3920
     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
3921
     * which usually contains relative references.
3922
     * These will be used to construct the formula in each shared formula part after the sheet is read.
3923
     */
3924
    private function readSharedFmla()
3925
    {
3926
        $length = self::getInt2d($this->data, $this->pos + 2);
3927
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3928
3929
        // move stream pointer to next record
3930
        $this->pos += 4 + $length;
3931
3932
        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
3933
        $cellRange = substr($recordData, 0, 6);
3934
        $cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
0 ignored issues
show
Unused Code introduced by
$cellRange 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...
3935
3936
        // offset: 6, size: 1; not used
3937
3938
        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
3939
        $no = ord($recordData[7]);
0 ignored issues
show
Unused Code introduced by
$no 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...
3940
3941
        // offset: 8, size: var; Binary token array of the shared formula
3942
        $formula = substr($recordData, 8);
3943
3944
        // at this point we only store the shared formula for later use
3945
        $this->sharedFormulas[$this->_baseCell] = $formula;
3946
    }
3947
3948
    /**
3949
     * Read a STRING record from current stream position and advance the stream pointer to next record
3950
     * This record is used for storing result from FORMULA record when it is a string, and
3951
     * it occurs directly after the FORMULA record.
3952
     *
3953
     * @return string The string contents as UTF-8
3954
     */
3955
    private function readString()
3956
    {
3957
        $length = self::getInt2d($this->data, $this->pos + 2);
3958
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3959
3960
        // move stream pointer to next record
3961
        $this->pos += 4 + $length;
3962
3963
        if ($this->version == self::XLS_BIFF8) {
3964
            $string = self::readUnicodeStringLong($recordData);
3965
            $value = $string['value'];
3966
        } else {
3967
            $string = $this->readByteStringLong($recordData);
3968
            $value = $string['value'];
3969
        }
3970
3971
        return $value;
3972
    }
3973
3974
    /**
3975
     * Read BOOLERR record
3976
     * This record represents a Boolean value or error value
3977
     * cell.
3978
     *
3979
     * --    "OpenOffice.org's Documentation of the Microsoft
3980
     *         Excel File Format"
3981
     */
3982
    private function readBoolErr()
3983
    {
3984
        $length = self::getInt2d($this->data, $this->pos + 2);
3985
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3986
3987
        // move stream pointer to next record
3988
        $this->pos += 4 + $length;
3989
3990
        // offset: 0; size: 2; row index
3991
        $row = self::getInt2d($recordData, 0);
3992
3993
        // offset: 2; size: 2; column index
3994
        $column = self::getInt2d($recordData, 2);
3995
        $columnString = Cell::stringFromColumnIndex($column);
3996
3997
        // Read cell?
3998
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3999
            // offset: 4; size: 2; index to XF record
4000
            $xfIndex = self::getInt2d($recordData, 4);
4001
4002
            // offset: 6; size: 1; the boolean value or error value
4003
            $boolErr = ord($recordData[6]);
4004
4005
            // offset: 7; size: 1; 0=boolean; 1=error
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
4006
            $isError = ord($recordData[7]);
4007
4008
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4009
            switch ($isError) {
4010
                case 0: // boolean
4011
                    $value = (bool) $boolErr;
4012
4013
                    // add cell value
4014
                    $cell->setValueExplicit($value, Cell\DataType::TYPE_BOOL);
4015
                    break;
4016
                case 1: // error type
4017
                    $value = Xls\ErrorCode::lookup($boolErr);
4018
4019
                    // add cell value
4020
                    $cell->setValueExplicit($value, Cell\DataType::TYPE_ERROR);
4021
                    break;
4022
            }
4023
4024
            if (!$this->readDataOnly) {
4025
                // add cell style
4026
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4027
            }
4028
        }
4029
    }
4030
4031
    /**
4032
     * Read MULBLANK record
4033
     * This record represents a cell range of empty cells. All
4034
     * cells are located in the same row.
4035
     *
4036
     * --    "OpenOffice.org's Documentation of the Microsoft
4037
     *         Excel File Format"
4038
     */
4039 1
    private function readMulBlank()
4040
    {
4041 1
        $length = self::getInt2d($this->data, $this->pos + 2);
4042 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4043
4044
        // move stream pointer to next record
4045 1
        $this->pos += 4 + $length;
4046
4047
        // offset: 0; size: 2; index to row
4048 1
        $row = self::getInt2d($recordData, 0);
4049
4050
        // offset: 2; size: 2; index to first column
4051 1
        $fc = self::getInt2d($recordData, 2);
4052
4053
        // offset: 4; size: 2 x nc; list of indexes to XF records
4054
        // add style information
4055 1
        if (!$this->readDataOnly && $this->readEmptyCells) {
4056 1
            for ($i = 0; $i < $length / 2 - 3; ++$i) {
4057 1
                $columnString = Cell::stringFromColumnIndex($fc + $i);
4058
4059
                // Read cell?
4060 1
                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4061 1
                    $xfIndex = self::getInt2d($recordData, 4 + 2 * $i);
4062 1
                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4063
                }
4064
            }
4065
        }
4066
4067
        // offset: 6; size 2; index to last column (not needed)
4068 1
    }
4069
4070
    /**
4071
     * Read LABEL record
4072
     * This record represents a cell that contains a string. In
4073
     * BIFF8 it is usually replaced by the LABELSST record.
4074
     * Excel still uses this record, if it copies unformatted
4075
     * text cells to the clipboard.
4076
     *
4077
     * --    "OpenOffice.org's Documentation of the Microsoft
4078
     *         Excel File Format"
4079
     */
4080
    private function readLabel()
4081
    {
4082
        $length = self::getInt2d($this->data, $this->pos + 2);
4083
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4084
4085
        // move stream pointer to next record
4086
        $this->pos += 4 + $length;
4087
4088
        // offset: 0; size: 2; index to row
4089
        $row = self::getInt2d($recordData, 0);
4090
4091
        // offset: 2; size: 2; index to column
4092
        $column = self::getInt2d($recordData, 2);
4093
        $columnString = Cell::stringFromColumnIndex($column);
4094
4095
        // Read cell?
4096
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4097
            // offset: 4; size: 2; XF index
4098
            $xfIndex = self::getInt2d($recordData, 4);
4099
4100
            // add cell value
4101
            // todo: what if string is very long? continue record
4102 View Code Duplication
            if ($this->version == self::XLS_BIFF8) {
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...
4103
                $string = self::readUnicodeStringLong(substr($recordData, 6));
4104
                $value = $string['value'];
4105
            } else {
4106
                $string = $this->readByteStringLong(substr($recordData, 6));
4107
                $value = $string['value'];
4108
            }
4109
            if ($this->readEmptyCells || trim($value) !== '') {
4110
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4111
                $cell->setValueExplicit($value, Cell\DataType::TYPE_STRING);
4112
4113
                if (!$this->readDataOnly) {
4114
                    // add cell style
4115
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4116
                }
4117
            }
4118
        }
4119
    }
4120
4121
    /**
4122
     * Read BLANK record.
4123
     */
4124 2
    private function readBlank()
4125
    {
4126 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4127 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4128
4129
        // move stream pointer to next record
4130 2
        $this->pos += 4 + $length;
4131
4132
        // offset: 0; size: 2; row index
4133 2
        $row = self::getInt2d($recordData, 0);
4134
4135
        // offset: 2; size: 2; col index
4136 2
        $col = self::getInt2d($recordData, 2);
4137 2
        $columnString = Cell::stringFromColumnIndex($col);
4138
4139
        // Read cell?
4140 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4141
            // offset: 4; size: 2; XF index
4142 2
            $xfIndex = self::getInt2d($recordData, 4);
4143
4144
            // add style information
4145 2
            if (!$this->readDataOnly && $this->readEmptyCells) {
4146 2
                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4147
            }
4148
        }
4149 2
    }
4150
4151
    /**
4152
     * Read MSODRAWING record.
4153
     */
4154 3 View Code Duplication
    private function readMsoDrawing()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
4155
    {
4156 3
        $length = self::getInt2d($this->data, $this->pos + 2);
0 ignored issues
show
Unused Code introduced by
$length 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...
4157
4158
        // get spliced record data
4159 3
        $splicedRecordData = $this->getSplicedRecordData();
4160 3
        $recordData = $splicedRecordData['recordData'];
4161
4162 3
        $this->drawingData .= $recordData;
4163 3
    }
4164
4165
    /**
4166
     * Read OBJ record.
4167
     */
4168 3
    private function readObj()
4169
    {
4170 3
        $length = self::getInt2d($this->data, $this->pos + 2);
4171 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4172
4173
        // move stream pointer to next record
4174 3
        $this->pos += 4 + $length;
4175
4176 3
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4177
            return;
4178
        }
4179
4180
        // recordData consists of an array of subrecords looking like this:
4181
        //    ft: 2 bytes; ftCmo type (0x15)
4182
        //    cb: 2 bytes; size in bytes of ftCmo data
4183
        //    ot: 2 bytes; Object Type
4184
        //    id: 2 bytes; Object id number
4185
        //    grbit: 2 bytes; Option Flags
4186
        //    data: var; subrecord data
4187
4188
        // for now, we are just interested in the second subrecord containing the object type
4189 3
        $ftCmoType = self::getInt2d($recordData, 0);
4190 3
        $cbCmoSize = self::getInt2d($recordData, 2);
4191 3
        $otObjType = self::getInt2d($recordData, 4);
4192 3
        $idObjID = self::getInt2d($recordData, 6);
4193 3
        $grbitOpts = self::getInt2d($recordData, 6);
4194
4195 3
        $this->objs[] = [
4196 3
            'ftCmoType' => $ftCmoType,
4197 3
            'cbCmoSize' => $cbCmoSize,
4198 3
            'otObjType' => $otObjType,
4199 3
            'idObjID' => $idObjID,
4200 3
            'grbitOpts' => $grbitOpts,
4201
        ];
4202 3
        $this->textObjRef = $idObjID;
4203 3
    }
4204
4205
    /**
4206
     * Read WINDOW2 record.
4207
     */
4208 4
    private function readWindow2()
4209
    {
4210 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4211 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4212
4213
        // move stream pointer to next record
4214 4
        $this->pos += 4 + $length;
4215
4216
        // offset: 0; size: 2; option flags
4217 4
        $options = self::getInt2d($recordData, 0);
4218
4219
        // offset: 2; size: 2; index to first visible row
4220 4
        $firstVisibleRow = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$firstVisibleRow 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...
4221
4222
        // offset: 4; size: 2; index to first visible colum
4223 4
        $firstVisibleColumn = self::getInt2d($recordData, 4);
0 ignored issues
show
Unused Code introduced by
$firstVisibleColumn 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...
4224 4
        if ($this->version === self::XLS_BIFF8) {
4225
            // offset:  8; size: 2; not used
4226
            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
4227
            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
4228
            // offset: 14; size: 4; not used
4229 4
            $zoomscaleInPageBreakPreview = self::getInt2d($recordData, 10);
4230 4
            if ($zoomscaleInPageBreakPreview === 0) {
4231 4
                $zoomscaleInPageBreakPreview = 60;
4232
            }
4233 4
            $zoomscaleInNormalView = self::getInt2d($recordData, 12);
4234 4
            if ($zoomscaleInNormalView === 0) {
4235 3
                $zoomscaleInNormalView = 100;
4236
            }
4237
        }
4238
4239
        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
4240 4
        $showGridlines = (bool) ((0x0002 & $options) >> 1);
4241 4
        $this->phpSheet->setShowGridlines($showGridlines);
4242
4243
        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
4244 4
        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4245 4
        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4246
4247
        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
4248 4
        $this->frozen = (bool) ((0x0008 & $options) >> 3);
4249
4250
        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
4251 4
        $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
4252
4253
        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
4254 4
        $isActive = (bool) ((0x0400 & $options) >> 10);
4255 4
        if ($isActive) {
4256 4
            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4257
        }
4258
4259
        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
4260 4
        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4261
4262
        //FIXME: set $firstVisibleRow and $firstVisibleColumn
4263
4264 4
        if ($this->phpSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_PAGE_LAYOUT) {
4265
            //NOTE: this setting is inferior to page layout view(Excel2007-)
4266 4
            $view = $isPageBreakPreview ? SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : SheetView::SHEETVIEW_NORMAL;
4267 4
            $this->phpSheet->getSheetView()->setView($view);
4268 4
            if ($this->version === self::XLS_BIFF8) {
4269 4
                $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
0 ignored issues
show
Bug introduced by
The variable $zoomscaleInPageBreakPreview 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...
Bug introduced by
The variable $zoomscaleInNormalView 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...
4270 4
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4271 4
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4272
            }
4273
        }
4274 4
    }
4275
4276
    /**
4277
     * Read PLV Record(Created by Excel2007 or upper).
4278
     */
4279 3
    private function readPageLayoutView()
4280
    {
4281 3
        $length = self::getInt2d($this->data, $this->pos + 2);
4282 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4283
4284
        // move stream pointer to next record
4285 3
        $this->pos += 4 + $length;
4286
4287
        // offset: 0; size: 2; rt
4288
        //->ignore
4289 3
        $rt = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$rt 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...
4290
        // offset: 2; size: 2; grbitfr
4291
        //->ignore
4292 3
        $grbitFrt = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$grbitFrt 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...
4293
        // offset: 4; size: 8; reserved
4294
        //->ignore
4295
4296
        // offset: 12; size 2; zoom scale
4297 3
        $wScalePLV = self::getInt2d($recordData, 12);
4298
        // offset: 14; size 2; grbit
4299 3
        $grbit = self::getInt2d($recordData, 14);
4300
4301
        // decomprise grbit
4302 3
        $fPageLayoutView = $grbit & 0x01;
4303 3
        $fRulerVisible = ($grbit >> 1) & 0x01; //no support
0 ignored issues
show
Unused Code introduced by
$fRulerVisible 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...
4304 3
        $fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
0 ignored issues
show
Unused Code introduced by
$fWhitespaceHidden 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...
4305
4306 3
        if ($fPageLayoutView === 1) {
4307
            $this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
4308
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4309
        }
4310
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4311 3
    }
4312
4313
    /**
4314
     * Read SCL record.
4315
     */
4316
    private function readScl()
4317
    {
4318
        $length = self::getInt2d($this->data, $this->pos + 2);
4319
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4320
4321
        // move stream pointer to next record
4322
        $this->pos += 4 + $length;
4323
4324
        // offset: 0; size: 2; numerator of the view magnification
4325
        $numerator = self::getInt2d($recordData, 0);
4326
4327
        // offset: 2; size: 2; numerator of the view magnification
4328
        $denumerator = self::getInt2d($recordData, 2);
4329
4330
        // set the zoom scale (in percent)
4331
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4332
    }
4333
4334
    /**
4335
     * Read PANE record.
4336
     */
4337
    private function readPane()
4338
    {
4339
        $length = self::getInt2d($this->data, $this->pos + 2);
4340
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4341
4342
        // move stream pointer to next record
4343
        $this->pos += 4 + $length;
4344
4345
        if (!$this->readDataOnly) {
4346
            // offset: 0; size: 2; position of vertical split
4347
            $px = self::getInt2d($recordData, 0);
4348
4349
            // offset: 2; size: 2; position of horizontal split
4350
            $py = self::getInt2d($recordData, 2);
4351
4352
            if ($this->frozen) {
4353
                // frozen panes
4354
                $this->phpSheet->freezePane(Cell::stringFromColumnIndex($px) . ($py + 1));
4355
            }
4356
                // unfrozen panes; split windows; not supported by PhpSpreadsheet core
4357
        }
4358
    }
4359
4360
    /**
4361
     * Read SELECTION record. There is one such record for each pane in the sheet.
4362
     */
4363 4
    private function readSelection()
4364
    {
4365 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4366 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4367
4368
        // move stream pointer to next record
4369 4
        $this->pos += 4 + $length;
4370
4371 4
        if (!$this->readDataOnly) {
4372
            // offset: 0; size: 1; pane identifier
4373 4
            $paneId = ord($recordData[0]);
0 ignored issues
show
Unused Code introduced by
$paneId 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...
4374
4375
            // offset: 1; size: 2; index to row of the active cell
4376 4
            $r = self::getInt2d($recordData, 1);
0 ignored issues
show
Unused Code introduced by
$r 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...
4377
4378
            // offset: 3; size: 2; index to column of the active cell
4379 4
            $c = self::getInt2d($recordData, 3);
0 ignored issues
show
Unused Code introduced by
$c 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...
4380
4381
            // offset: 5; size: 2; index into the following cell range list to the
4382
            //  entry that contains the active cell
4383 4
            $index = self::getInt2d($recordData, 5);
0 ignored issues
show
Unused Code introduced by
$index 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...
4384
4385
            // offset: 7; size: var; cell range address list containing all selected cell ranges
4386 4
            $data = substr($recordData, 7);
4387 4
            $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
4388
4389 4
            $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
4390
4391
            // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
4392 4
            if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
4393
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
4394
            }
4395
4396
            // first row '1' + last row '65536' indicates that full column is selected
4397 4
            if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
4398
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
4399
            }
4400
4401
            // first column 'A' + last column 'IV' indicates that full row is selected
4402 4
            if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
4403
                $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
4404
            }
4405
4406 4
            $this->phpSheet->setSelectedCells($selectedCells);
4407
        }
4408 4
    }
4409
4410 2
    private function includeCellRangeFiltered($cellRangeAddress)
4411
    {
4412 2
        $includeCellRange = true;
4413 2
        if ($this->getReadFilter() !== null) {
4414 2
            $includeCellRange = false;
4415 2
            $rangeBoundaries = Cell::getRangeBoundaries($cellRangeAddress);
4416 2
            ++$rangeBoundaries[1][0];
4417 2
            for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
4418 2
                for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
4419 2
                    if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
4420 2
                        $includeCellRange = true;
4421 2
                        break 2;
4422
                    }
4423
                }
4424
            }
4425
        }
4426
4427 2
        return $includeCellRange;
4428
    }
4429
4430
    /**
4431
     * MERGEDCELLS.
4432
     *
4433
     * This record contains the addresses of merged cell ranges
4434
     * in the current sheet.
4435
     *
4436
     * --    "OpenOffice.org's Documentation of the Microsoft
4437
     *         Excel File Format"
4438
     */
4439 2
    private function readMergedCells()
4440
    {
4441 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4442 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4443
4444
        // move stream pointer to next record
4445 2
        $this->pos += 4 + $length;
4446
4447 2
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
4448 2
            $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
4449 2
            foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
4450 2
                if ((strpos($cellRangeAddress, ':') !== false) &&
4451 2
                    ($this->includeCellRangeFiltered($cellRangeAddress))) {
4452 2
                    $this->phpSheet->mergeCells($cellRangeAddress);
4453
                }
4454
            }
4455
        }
4456 2
    }
4457
4458
    /**
4459
     * Read HYPERLINK record.
4460
     */
4461 2
    private function readHyperLink()
4462
    {
4463 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4464 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4465
4466
        // move stream pointer forward to next record
4467 2
        $this->pos += 4 + $length;
4468
4469 2
        if (!$this->readDataOnly) {
4470
            // offset: 0; size: 8; cell range address of all cells containing this hyperlink
4471
            try {
4472 2
                $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
4473
            } catch (PhpSpreadsheetException $e) {
4474
                return;
4475
            }
4476
4477
            // offset: 8, size: 16; GUID of StdLink
4478
4479
            // offset: 24, size: 4; unknown value
4480
4481
            // offset: 28, size: 4; option flags
4482
            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
4483 2
            $isFileLinkOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 0;
4484
4485
            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
4486 2
            $isAbsPathOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 1;
0 ignored issues
show
Unused Code introduced by
$isAbsPathOrUrl 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...
4487
4488
            // bit: 2 (and 4); mask: 0x00000014; 0 = no description
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
4489 2
            $hasDesc = (0x00000014 & self::getInt2d($recordData, 28)) >> 2;
4490
4491
            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
4492 2
            $hasText = (0x00000008 & self::getInt2d($recordData, 28)) >> 3;
4493
4494
            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
4495 2
            $hasFrame = (0x00000080 & self::getInt2d($recordData, 28)) >> 7;
4496
4497
            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
4498 2
            $isUNC = (0x00000100 & self::getInt2d($recordData, 28)) >> 8;
4499
4500
            // offset within record data
4501 2
            $offset = 32;
4502
4503 2
            if ($hasDesc) {
4504
                // offset: 32; size: var; character count of description text
4505 1
                $dl = self::getInt4d($recordData, 32);
4506
                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
4507 1
                $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
0 ignored issues
show
Unused Code introduced by
$desc 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...
4508 1
                $offset += 4 + 2 * $dl;
4509
            }
4510 2
            if ($hasFrame) {
4511
                $fl = self::getInt4d($recordData, $offset);
4512
                $offset += 4 + 2 * $fl;
4513
            }
4514
4515
            // detect type of hyperlink (there are 4 types)
4516 2
            $hyperlinkType = null;
4517
4518 2
            if ($isUNC) {
4519
                $hyperlinkType = 'UNC';
4520 2
            } elseif (!$isFileLinkOrUrl) {
4521 2
                $hyperlinkType = 'workbook';
4522 2
            } elseif (ord($recordData[$offset]) == 0x03) {
4523
                $hyperlinkType = 'local';
4524 2
            } elseif (ord($recordData[$offset]) == 0xE0) {
4525 2
                $hyperlinkType = 'URL';
4526
            }
4527
4528
            switch ($hyperlinkType) {
4529 2
                case 'URL':
4530
                    // section 5.58.2: Hyperlink containing a URL
4531
                    // e.g. http://example.org/index.php
4532
4533
                    // offset: var; size: 16; GUID of URL Moniker
4534 2
                    $offset += 16;
4535
                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
4536 2
                    $us = self::getInt4d($recordData, $offset);
4537 2
                    $offset += 4;
4538
                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
4539 2
                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
4540 2
                    $nullOffset = strpos($url, 0x00);
4541 2
                    if ($nullOffset) {
4542 1
                        $url = substr($url, 0, $nullOffset);
4543
                    }
4544 2
                    $url .= $hasText ? '#' : '';
4545 2
                    $offset += $us;
4546 2
                    break;
4547 2
                case 'local':
4548
                    // section 5.58.3: Hyperlink to local file
4549
                    // examples:
4550
                    //   mydoc.txt
4551
                    //   ../../somedoc.xls#Sheet!A1
4552
4553
                    // offset: var; size: 16; GUI of File Moniker
4554
                    $offset += 16;
4555
4556
                    // offset: var; size: 2; directory up-level count.
4557
                    $upLevelCount = self::getInt2d($recordData, $offset);
4558
                    $offset += 2;
4559
4560
                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
4561
                    $sl = self::getInt4d($recordData, $offset);
4562
                    $offset += 4;
4563
4564
                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
4565
                    $shortenedFilePath = substr($recordData, $offset, $sl);
4566
                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
4567
                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
4568
4569
                    $offset += $sl;
4570
4571
                    // offset: var; size: 24; unknown sequence
4572
                    $offset += 24;
4573
4574
                    // extended file path
4575
                    // offset: var; size: 4; size of the following file link field including string lenth mark
4576
                    $sz = self::getInt4d($recordData, $offset);
4577
                    $offset += 4;
4578
4579
                    // only present if $sz > 0
4580
                    if ($sz > 0) {
4581
                        // offset: var; size: 4; size of the character array of the extended file path and name
4582
                        $xl = self::getInt4d($recordData, $offset);
4583
                        $offset += 4;
4584
4585
                        // offset: var; size 2; unknown
4586
                        $offset += 2;
4587
4588
                        // offset: var; size $xl; character array of the extended file path and name.
4589
                        $extendedFilePath = substr($recordData, $offset, $xl);
4590
                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
4591
                        $offset += $xl;
4592
                    }
4593
4594
                    // construct the path
4595
                    $url = str_repeat('..\\', $upLevelCount);
4596
                    $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available
0 ignored issues
show
Bug introduced by
The variable $extendedFilePath 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...
4597
                    $url .= $hasText ? '#' : '';
4598
4599
                    break;
4600 2
                case 'UNC':
4601
                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
4602
                    // todo: implement
4603
                    return;
4604 2
                case 'workbook':
4605
                    // section 5.58.5: Hyperlink to the Current Workbook
4606
                    // e.g. Sheet2!B1:C2, stored in text mark field
4607 2
                    $url = 'sheet://';
4608 2
                    break;
4609
                default:
4610
                    return;
4611
            }
4612
4613 2
            if ($hasText) {
4614
                // offset: var; size: 4; character count of text mark including trailing zero word
4615 2
                $tl = self::getInt4d($recordData, $offset);
4616 2
                $offset += 4;
4617
                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
4618 2
                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
4619 2
                $url .= $text;
4620
            }
4621
4622
            // apply the hyperlink to all the relevant cells
4623 2
            foreach (Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
4624 2
                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
4625
            }
4626
        }
4627 2
    }
4628
4629
    /**
4630
     * Read DATAVALIDATIONS record.
4631
     */
4632
    private function readDataValidations()
4633
    {
4634
        $length = self::getInt2d($this->data, $this->pos + 2);
4635
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
0 ignored issues
show
Unused Code introduced by
$recordData 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...
4636
4637
        // move stream pointer forward to next record
4638
        $this->pos += 4 + $length;
4639
    }
4640
4641
    /**
4642
     * Read DATAVALIDATION record.
4643
     */
4644
    private function readDataValidation()
4645
    {
4646
        $length = self::getInt2d($this->data, $this->pos + 2);
4647
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4648
4649
        // move stream pointer forward to next record
4650
        $this->pos += 4 + $length;
4651
4652
        if ($this->readDataOnly) {
4653
            return;
4654
        }
4655
4656
        // offset: 0; size: 4; Options
4657
        $options = self::getInt4d($recordData, 0);
4658
4659
        // bit: 0-3; mask: 0x0000000F; type
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
4660
        $type = (0x0000000F & $options) >> 0;
4661 View Code Duplication
        switch ($type) {
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...
4662
            case 0x00:
4663
                $type = DataValidation::TYPE_NONE;
4664
                break;
4665
            case 0x01:
4666
                $type = DataValidation::TYPE_WHOLE;
4667
                break;
4668
            case 0x02:
4669
                $type = DataValidation::TYPE_DECIMAL;
4670
                break;
4671
            case 0x03:
4672
                $type = DataValidation::TYPE_LIST;
4673
                break;
4674
            case 0x04:
4675
                $type = DataValidation::TYPE_DATE;
4676
                break;
4677
            case 0x05:
4678
                $type = DataValidation::TYPE_TIME;
4679
                break;
4680
            case 0x06:
4681
                $type = DataValidation::TYPE_TEXTLENGTH;
4682
                break;
4683
            case 0x07:
4684
                $type = DataValidation::TYPE_CUSTOM;
4685
                break;
4686
        }
4687
4688
        // bit: 4-6; mask: 0x00000070; error type
4689
        $errorStyle = (0x00000070 & $options) >> 4;
4690 View Code Duplication
        switch ($errorStyle) {
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...
4691
            case 0x00:
4692
                $errorStyle = DataValidation::STYLE_STOP;
4693
                break;
4694
            case 0x01:
4695
                $errorStyle = DataValidation::STYLE_WARNING;
4696
                break;
4697
            case 0x02:
4698
                $errorStyle = DataValidation::STYLE_INFORMATION;
4699
                break;
4700
        }
4701
4702
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
4703
        // I have only seen cases where this is 1
4704
        $explicitFormula = (0x00000080 & $options) >> 7;
0 ignored issues
show
Unused Code introduced by
$explicitFormula 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...
4705
4706
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
4707
        $allowBlank = (0x00000100 & $options) >> 8;
4708
4709
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
4710
        $suppressDropDown = (0x00000200 & $options) >> 9;
4711
4712
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
4713
        $showInputMessage = (0x00040000 & $options) >> 18;
4714
4715
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
4716
        $showErrorMessage = (0x00080000 & $options) >> 19;
4717
4718
        // bit: 20-23; mask: 0x00F00000; condition operator
4719
        $operator = (0x00F00000 & $options) >> 20;
4720 View Code Duplication
        switch ($operator) {
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...
4721
            case 0x00:
4722
                $operator = DataValidation::OPERATOR_BETWEEN;
4723
                break;
4724
            case 0x01:
4725
                $operator = DataValidation::OPERATOR_NOTBETWEEN;
4726
                break;
4727
            case 0x02:
4728
                $operator = DataValidation::OPERATOR_EQUAL;
4729
                break;
4730
            case 0x03:
4731
                $operator = DataValidation::OPERATOR_NOTEQUAL;
4732
                break;
4733
            case 0x04:
4734
                $operator = DataValidation::OPERATOR_GREATERTHAN;
4735
                break;
4736
            case 0x05:
4737
                $operator = DataValidation::OPERATOR_LESSTHAN;
4738
                break;
4739
            case 0x06:
4740
                $operator = DataValidation::OPERATOR_GREATERTHANOREQUAL;
4741
                break;
4742
            case 0x07:
4743
                $operator = DataValidation::OPERATOR_LESSTHANOREQUAL;
4744
                break;
4745
        }
4746
4747
        // offset: 4; size: var; title of the prompt box
4748
        $offset = 4;
4749
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4750
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4751
        $offset += $string['size'];
4752
4753
        // offset: var; size: var; title of the error box
4754
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4755
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4756
        $offset += $string['size'];
4757
4758
        // offset: var; size: var; text of the prompt box
4759
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4760
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
4761
        $offset += $string['size'];
4762
4763
        // offset: var; size: var; text of the error box
4764
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4765
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
4766
        $offset += $string['size'];
4767
4768
        // offset: var; size: 2; size of the formula data for the first condition
4769
        $sz1 = self::getInt2d($recordData, $offset);
4770
        $offset += 2;
4771
4772
        // offset: var; size: 2; not used
4773
        $offset += 2;
4774
4775
        // offset: var; size: $sz1; formula data for first condition (without size field)
4776
        $formula1 = substr($recordData, $offset, $sz1);
4777
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
4778
        try {
4779
            $formula1 = $this->getFormulaFromStructure($formula1);
4780
4781
            // in list type validity, null characters are used as item separators
4782
            if ($type == DataValidation::TYPE_LIST) {
4783
                $formula1 = str_replace(chr(0), ',', $formula1);
4784
            }
4785
        } catch (PhpSpreadsheetException $e) {
4786
            return;
4787
        }
4788
        $offset += $sz1;
4789
4790
        // offset: var; size: 2; size of the formula data for the first condition
4791
        $sz2 = self::getInt2d($recordData, $offset);
4792
        $offset += 2;
4793
4794
        // offset: var; size: 2; not used
4795
        $offset += 2;
4796
4797
        // offset: var; size: $sz2; formula data for second condition (without size field)
4798
        $formula2 = substr($recordData, $offset, $sz2);
4799
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
4800
        try {
4801
            $formula2 = $this->getFormulaFromStructure($formula2);
4802
        } catch (PhpSpreadsheetException $e) {
4803
            return;
4804
        }
4805
        $offset += $sz2;
4806
4807
        // offset: var; size: var; cell range address list with
4808
        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
4809
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
4810
4811
        foreach ($cellRangeAddresses as $cellRange) {
4812
            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4813
            foreach (Cell::extractAllCellReferencesInRange($stRange) as $coordinate) {
4814
                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4815
                $objValidation->setType($type);
4816
                $objValidation->setErrorStyle($errorStyle);
4817
                $objValidation->setAllowBlank((bool) $allowBlank);
4818
                $objValidation->setShowInputMessage((bool) $showInputMessage);
4819
                $objValidation->setShowErrorMessage((bool) $showErrorMessage);
4820
                $objValidation->setShowDropDown(!$suppressDropDown);
4821
                $objValidation->setOperator($operator);
4822
                $objValidation->setErrorTitle($errorTitle);
4823
                $objValidation->setError($error);
4824
                $objValidation->setPromptTitle($promptTitle);
4825
                $objValidation->setPrompt($prompt);
4826
                $objValidation->setFormula1($formula1);
4827
                $objValidation->setFormula2($formula2);
4828
            }
4829
        }
4830
    }
4831
4832
    /**
4833
     * Read SHEETLAYOUT record. Stores sheet tab color information.
4834
     */
4835 2
    private function readSheetLayout()
4836
    {
4837 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4838 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4839
4840
        // move stream pointer to next record
4841 2
        $this->pos += 4 + $length;
4842
4843
        // local pointer in record data
4844 2
        $offset = 0;
0 ignored issues
show
Unused Code introduced by
$offset 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...
4845
4846 2
        if (!$this->readDataOnly) {
4847
            // offset: 0; size: 2; repeated record identifier 0x0862
4848
4849
            // offset: 2; size: 10; not used
4850
4851
            // offset: 12; size: 4; size of record data
4852
            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
4853 2
            $sz = self::getInt4d($recordData, 12);
4854
4855
            switch ($sz) {
4856 2
                case 0x14:
4857
                    // offset: 16; size: 2; color index for sheet tab
4858 1
                    $colorIndex = self::getInt2d($recordData, 16);
4859 1
                    $color = Xls\Color::map($colorIndex, $this->palette, $this->version);
4860 1
                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
4861 1
                    break;
4862 1
                case 0x28:
4863
                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
4864 1
                    return;
4865
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
4866
            }
4867
        }
4868 1
    }
4869
4870
    /**
4871
     * Read SHEETPROTECTION record (FEATHEADR).
4872
     */
4873 4
    private function readSheetProtection()
4874
    {
4875 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4876 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4877
4878
        // move stream pointer to next record
4879 4
        $this->pos += 4 + $length;
4880
4881 4
        if ($this->readDataOnly) {
4882
            return;
4883
        }
4884
4885
        // offset: 0; size: 2; repeated record header
4886
4887
        // offset: 2; size: 2; FRT cell reference flag (=0 currently)
4888
4889
        // offset: 4; size: 8; Currently not used and set to 0
4890
4891
        // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
4892 4
        $isf = self::getInt2d($recordData, 12);
4893 4
        if ($isf != 2) {
4894
            return;
4895
        }
4896
4897
        // offset: 14; size: 1; =1 since this is a feat header
4898
4899
        // offset: 15; size: 4; size of rgbHdrSData
4900
4901
        // rgbHdrSData, assume "Enhanced Protection"
4902
        // offset: 19; size: 2; option flags
4903 4
        $options = self::getInt2d($recordData, 19);
4904
4905
        // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
4906 4
        $bool = (0x0001 & $options) >> 0;
4907 4
        $this->phpSheet->getProtection()->setObjects(!$bool);
4908
4909
        // bit: 1; mask 0x0002; edit scenarios
4910 4
        $bool = (0x0002 & $options) >> 1;
4911 4
        $this->phpSheet->getProtection()->setScenarios(!$bool);
4912
4913
        // bit: 2; mask 0x0004; format cells
4914 4
        $bool = (0x0004 & $options) >> 2;
4915 4
        $this->phpSheet->getProtection()->setFormatCells(!$bool);
4916
4917
        // bit: 3; mask 0x0008; format columns
4918 4
        $bool = (0x0008 & $options) >> 3;
4919 4
        $this->phpSheet->getProtection()->setFormatColumns(!$bool);
4920
4921
        // bit: 4; mask 0x0010; format rows
4922 4
        $bool = (0x0010 & $options) >> 4;
4923 4
        $this->phpSheet->getProtection()->setFormatRows(!$bool);
4924
4925
        // bit: 5; mask 0x0020; insert columns
4926 4
        $bool = (0x0020 & $options) >> 5;
4927 4
        $this->phpSheet->getProtection()->setInsertColumns(!$bool);
4928
4929
        // bit: 6; mask 0x0040; insert rows
4930 4
        $bool = (0x0040 & $options) >> 6;
4931 4
        $this->phpSheet->getProtection()->setInsertRows(!$bool);
4932
4933
        // bit: 7; mask 0x0080; insert hyperlinks
4934 4
        $bool = (0x0080 & $options) >> 7;
4935 4
        $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
4936
4937
        // bit: 8; mask 0x0100; delete columns
4938 4
        $bool = (0x0100 & $options) >> 8;
4939 4
        $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
4940
4941
        // bit: 9; mask 0x0200; delete rows
4942 4
        $bool = (0x0200 & $options) >> 9;
4943 4
        $this->phpSheet->getProtection()->setDeleteRows(!$bool);
4944
4945
        // bit: 10; mask 0x0400; select locked cells
4946 4
        $bool = (0x0400 & $options) >> 10;
4947 4
        $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
4948
4949
        // bit: 11; mask 0x0800; sort cell range
4950 4
        $bool = (0x0800 & $options) >> 11;
4951 4
        $this->phpSheet->getProtection()->setSort(!$bool);
4952
4953
        // bit: 12; mask 0x1000; auto filter
4954 4
        $bool = (0x1000 & $options) >> 12;
4955 4
        $this->phpSheet->getProtection()->setAutoFilter(!$bool);
4956
4957
        // bit: 13; mask 0x2000; pivot tables
4958 4
        $bool = (0x2000 & $options) >> 13;
4959 4
        $this->phpSheet->getProtection()->setPivotTables(!$bool);
4960
4961
        // bit: 14; mask 0x4000; select unlocked cells
4962 4
        $bool = (0x4000 & $options) >> 14;
4963 4
        $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
4964
4965
        // offset: 21; size: 2; not used
4966 4
    }
4967
4968
    /**
4969
     * Read RANGEPROTECTION record
4970
     * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
4971
     * where it is referred to as FEAT record.
4972
     */
4973 1
    private function readRangeProtection()
4974
    {
4975 1
        $length = self::getInt2d($this->data, $this->pos + 2);
4976 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4977
4978
        // move stream pointer to next record
4979 1
        $this->pos += 4 + $length;
4980
4981
        // local pointer in record data
4982 1
        $offset = 0;
4983
4984 1
        if (!$this->readDataOnly) {
4985 1
            $offset += 12;
4986
4987
            // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
4988 1
            $isf = self::getInt2d($recordData, 12);
4989 1
            if ($isf != 2) {
4990
                // we only read FEAT records of type 2
4991
                return;
4992
            }
4993 1
            $offset += 2;
4994
4995 1
            $offset += 5;
4996
4997
            // offset: 19; size: 2; count of ref ranges this feature is on
4998 1
            $cref = self::getInt2d($recordData, 19);
4999 1
            $offset += 2;
5000
5001 1
            $offset += 6;
5002
5003
            // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
5004 1
            $cellRanges = [];
5005 1
            for ($i = 0; $i < $cref; ++$i) {
5006
                try {
5007 1
                    $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
5008
                } catch (PhpSpreadsheetException $e) {
5009
                    return;
5010
                }
5011 1
                $cellRanges[] = $cellRange;
5012 1
                $offset += 8;
5013
            }
5014
5015
            // offset: var; size: var; variable length of feature specific data
5016 1
            $rgbFeat = substr($recordData, $offset);
0 ignored issues
show
Unused Code introduced by
$rgbFeat 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...
5017 1
            $offset += 4;
5018
5019
            // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
5020 1
            $wPassword = self::getInt4d($recordData, $offset);
5021 1
            $offset += 4;
5022
5023
            // Apply range protection to sheet
5024 1
            if ($cellRanges) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cellRanges of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
5025 1
                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
5026
            }
5027
        }
5028 1
    }
5029
5030
    /**
5031
     * Read IMDATA record.
5032
     */
5033
    private function readImData()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
5034
    {
5035
        $length = self::getInt2d($this->data, $this->pos + 2);
0 ignored issues
show
Unused Code introduced by
$length 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...
5036
5037
        // get spliced record data
5038
        $splicedRecordData = $this->getSplicedRecordData();
5039
        $recordData = $splicedRecordData['recordData'];
5040
5041
        // UNDER CONSTRUCTION
5042
5043
        // offset: 0; size: 2; image format
5044
        $cf = self::getInt2d($recordData, 0);
5045
5046
        // offset: 2; size: 2; environment from which the file was written
5047
        $env = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$env 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...
5048
5049
        // offset: 4; size: 4; length of the image data
5050
        $lcb = self::getInt4d($recordData, 4);
0 ignored issues
show
Unused Code introduced by
$lcb 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...
5051
5052
        // offset: 8; size: var; image data
5053
        $iData = substr($recordData, 8);
5054
5055
        switch ($cf) {
5056
            case 0x09: // Windows bitmap format
5057
                // BITMAPCOREINFO
5058
                // 1. BITMAPCOREHEADER
5059
                // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
5060
                $bcSize = self::getInt4d($iData, 0);
0 ignored issues
show
Unused Code introduced by
$bcSize 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...
5061
5062
                // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
5063
                $bcWidth = self::getInt2d($iData, 4);
5064
5065
                // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
5066
                $bcHeight = self::getInt2d($iData, 6);
5067
                $ih = imagecreatetruecolor($bcWidth, $bcHeight);
5068
5069
                // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
5070
5071
                // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
5072
                $bcBitCount = self::getInt2d($iData, 10);
0 ignored issues
show
Unused Code introduced by
$bcBitCount 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...
5073
5074
                $rgbString = substr($iData, 12);
5075
                $rgbTriples = [];
5076
                while (strlen($rgbString) > 0) {
5077
                    $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
5078
                    $rgbString = substr($rgbString, 3);
5079
                }
5080
                $x = 0;
5081
                $y = 0;
5082
                foreach ($rgbTriples as $i => $rgbTriple) {
5083
                    $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
5084
                    imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
5085
                    $x = ($x + 1) % $bcWidth;
5086
                    $y = $y + floor(($x + 1) / $bcWidth);
5087
                }
5088
                //imagepng($ih, 'image.png');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
5089
5090
                $drawing = new Drawing();
5091
                $drawing->setPath($filename);
0 ignored issues
show
Bug introduced by
The variable $filename does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
5092
                $drawing->setWorksheet($this->phpSheet);
5093
                break;
5094
            case 0x02: // Windows metafile or Macintosh PICT format
5095
            case 0x0e: // native format
5096
            default:
5097
                break;
5098
        }
5099
5100
        // getSplicedRecordData() takes care of moving current position in data stream
5101
    }
5102
5103
    /**
5104
     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
5105
     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
5106
     * In this case, we must treat the CONTINUE record as a MSODRAWING record.
5107
     */
5108
    private function readContinue()
5109
    {
5110
        $length = self::getInt2d($this->data, $this->pos + 2);
5111
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5112
5113
        // check if we are reading drawing data
5114
        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
5115
        if ($this->drawingData == '') {
5116
            // move stream pointer to next record
5117
            $this->pos += 4 + $length;
5118
5119
            return;
5120
        }
5121
5122
        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
5123
        if ($length < 4) {
5124
            // move stream pointer to next record
5125
            $this->pos += 4 + $length;
5126
5127
            return;
5128
        }
5129
5130
        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
5131
        // look inside CONTINUE record to see if it looks like a part of an Escher stream
5132
        // we know that Escher stream may be split at least at
5133
        //        0xF003 MsofbtSpgrContainer
5134
        //        0xF004 MsofbtSpContainer
5135
        //        0xF00D MsofbtClientTextbox
5136
        $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
5137
5138
        $splitPoint = self::getInt2d($recordData, 2);
5139
        if (in_array($splitPoint, $validSplitPoints)) {
5140
            // get spliced record data (and move pointer to next record)
5141
            $splicedRecordData = $this->getSplicedRecordData();
5142
            $this->drawingData .= $splicedRecordData['recordData'];
5143
5144
            return;
5145
        }
5146
5147
        // move stream pointer to next record
5148
        $this->pos += 4 + $length;
5149
    }
5150
5151
    /**
5152
     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
5153
     * records are found. Splices the record data pieces and returns the combined string as if record data
5154
     * is in one piece.
5155
     * Moves to next current position in data stream to start of next record different from a CONtINUE record.
5156
     *
5157
     * @return array
5158
     */
5159 4
    private function getSplicedRecordData()
5160
    {
5161 4
        $data = '';
5162 4
        $spliceOffsets = [];
5163
5164 4
        $i = 0;
5165 4
        $spliceOffsets[0] = 0;
5166
5167
        do {
5168 4
            ++$i;
5169
5170
            // offset: 0; size: 2; identifier
5171 4
            $identifier = self::getInt2d($this->data, $this->pos);
0 ignored issues
show
Unused Code introduced by
$identifier 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...
5172
            // offset: 2; size: 2; length
5173 4
            $length = self::getInt2d($this->data, $this->pos + 2);
5174 4
            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
5175
5176 4
            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
5177
5178 4
            $this->pos += 4 + $length;
5179 4
            $nextIdentifier = self::getInt2d($this->data, $this->pos);
5180 4
        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5181
5182
        $splicedData = [
5183 4
            'recordData' => $data,
5184 4
            'spliceOffsets' => $spliceOffsets,
5185
        ];
5186
5187 4
        return $splicedData;
5188
    }
5189
5190
    /**
5191
     * Convert formula structure into human readable Excel formula like 'A3+A5*5'.
5192
     *
5193
     * @param string $formulaStructure The complete binary data for the formula
5194
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5195
     *
5196
     * @return string Human readable formula
5197
     */
5198 2
    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
5199
    {
5200
        // offset: 0; size: 2; size of the following formula data
5201 2
        $sz = self::getInt2d($formulaStructure, 0);
5202
5203
        // offset: 2; size: sz
5204 2
        $formulaData = substr($formulaStructure, 2, $sz);
5205
5206
        // offset: 2 + sz; size: variable (optional)
5207 2
        if (strlen($formulaStructure) > 2 + $sz) {
5208
            $additionalData = substr($formulaStructure, 2 + $sz);
5209
        } else {
5210 2
            $additionalData = '';
5211
        }
5212
5213 2
        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
5214
    }
5215
5216
    /**
5217
     * Take formula data and additional data for formula and return human readable formula.
5218
     *
5219
     * @param string $formulaData The binary data for the formula itself
5220
     * @param string $additionalData Additional binary data going with the formula
5221
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5222
     *
5223
     * @return string Human readable formula
5224
     */
5225 2
    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
5226
    {
5227
        // start parsing the formula data
5228 2
        $tokens = [];
5229
5230 2
        while (strlen($formulaData) > 0 and $token = $this->getNextToken($formulaData, $baseCell)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
5231 2
            $tokens[] = $token;
5232 2
            $formulaData = substr($formulaData, $token['size']);
5233
        }
5234
5235 2
        $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
5236
5237 2
        return $formulaString;
5238
    }
5239
5240
    /**
5241
     * Take array of tokens together with additional data for formula and return human readable formula.
5242
     *
5243
     * @param array $tokens
5244
     * @param string $additionalData Additional binary data going with the formula
5245
     *
5246
     * @return string Human readable formula
5247
     */
5248 2
    private function createFormulaFromTokens($tokens, $additionalData)
5249
    {
5250
        // empty formula?
5251 2
        if (empty($tokens)) {
5252
            return '';
5253
        }
5254
5255 2
        $formulaStrings = [];
5256 2
        foreach ($tokens as $token) {
5257
            // initialize spaces
5258 2
            $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
5259 2
            $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
5260 2
            $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
5261 2
            $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
5262 2
            $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
5263 2
            $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
5264
5265 2
            switch ($token['name']) {
5266 2
                case 'tAdd': // addition
5267 2
                case 'tConcat': // addition
5268 2
                case 'tDiv': // division
5269 2
                case 'tEQ': // equality
5270 2
                case 'tGE': // greater than or equal
5271 2
                case 'tGT': // greater than
5272 2
                case 'tIsect': // intersection
5273 2
                case 'tLE': // less than or equal
5274 2
                case 'tList': // less than or equal
5275 2
                case 'tLT': // less than
5276 2
                case 'tMul': // multiplication
5277 2
                case 'tNE': // multiplication
5278 2
                case 'tPower': // power
5279 2
                case 'tRange': // range
5280 2
                case 'tSub': // subtraction
5281 2
                    $op2 = array_pop($formulaStrings);
5282 2
                    $op1 = array_pop($formulaStrings);
5283 2
                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
5284 2
                    unset($space0, $space1);
5285 2
                    break;
5286 2
                case 'tUplus': // unary plus
5287 2 View Code Duplication
                case 'tUminus': // unary minus
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...
5288
                    $op = array_pop($formulaStrings);
5289
                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
5290
                    unset($space0, $space1);
5291
                    break;
5292 2 View Code Duplication
                case 'tPercent': // percent sign
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...
5293
                    $op = array_pop($formulaStrings);
5294
                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
5295
                    unset($space0, $space1);
5296
                    break;
5297 2
                case 'tAttrVolatile': // indicates volatile function
5298 2
                case 'tAttrIf':
5299 2
                case 'tAttrSkip':
5300 2
                case 'tAttrChoose':
5301
                    // token is only important for Excel formula evaluator
5302
                    // do nothing
5303
                    break;
5304 2
                case 'tAttrSpace': // space / carriage return
5305
                    // space will be used when next token arrives, do not alter formulaString stack
5306
                    switch ($token['data']['spacetype']) {
5307
                        case 'type0':
5308
                            $space0 = str_repeat(' ', $token['data']['spacecount']);
5309
                            break;
5310
                        case 'type1':
5311
                            $space1 = str_repeat("\n", $token['data']['spacecount']);
5312
                            break;
5313
                        case 'type2':
5314
                            $space2 = str_repeat(' ', $token['data']['spacecount']);
5315
                            break;
5316
                        case 'type3':
5317
                            $space3 = str_repeat("\n", $token['data']['spacecount']);
5318
                            break;
5319
                        case 'type4':
5320
                            $space4 = str_repeat(' ', $token['data']['spacecount']);
5321
                            break;
5322
                        case 'type5':
5323
                            $space5 = str_repeat("\n", $token['data']['spacecount']);
5324
                            break;
5325
                    }
5326
                    break;
5327 2
                case 'tAttrSum': // SUM function with one parameter
5328 1
                    $op = array_pop($formulaStrings);
5329 1
                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
5330 1
                    unset($space0, $space1);
5331 1
                    break;
5332 2
                case 'tFunc': // function with fixed number of arguments
5333 2
                case 'tFuncV': // function with variable number of arguments
5334 1
                    if ($token['data']['function'] != '') {
5335
                        // normal function
5336 1
                        $ops = []; // array of operators
5337 1 View Code Duplication
                        for ($i = 0; $i < $token['data']['args']; ++$i) {
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...
5338 1
                            $ops[] = array_pop($formulaStrings);
5339
                        }
5340 1
                        $ops = array_reverse($ops);
5341 1
                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
5342 1
                        unset($space0, $space1);
5343
                    } else {
5344
                        // add-in function
5345
                        $ops = []; // array of operators
5346 View Code Duplication
                        for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
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...
5347
                            $ops[] = array_pop($formulaStrings);
5348
                        }
5349
                        $ops = array_reverse($ops);
5350
                        $function = array_pop($formulaStrings);
5351
                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
5352
                        unset($space0, $space1);
5353
                    }
5354 1
                    break;
5355 2
                case 'tParen': // parenthesis
5356
                    $expression = array_pop($formulaStrings);
5357
                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
5358
                    unset($space2, $space3, $space4, $space5);
5359
                    break;
5360 2
                case 'tArray': // array constant
5361
                    $constantArray = self::readBIFF8ConstantArray($additionalData);
5362
                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
5363
                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
5364
                    unset($space0, $space1);
5365
                    break;
5366 2
                case 'tMemArea':
5367
                    // bite off chunk of additional data
5368
                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
5369
                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
5370
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5371
                    unset($space0, $space1);
5372
                    break;
5373 2
                case 'tArea': // cell range address
5374 2
                case 'tBool': // boolean
5375 2
                case 'tErr': // error code
5376 2
                case 'tInt': // integer
5377 2
                case 'tMemErr':
5378 2
                case 'tMemFunc':
5379 2
                case 'tMissArg':
5380 2
                case 'tName':
5381 2
                case 'tNameX':
5382 2
                case 'tNum': // number
5383 2
                case 'tRef': // single cell reference
5384 2
                case 'tRef3d': // 3d cell reference
5385 2
                case 'tArea3d': // 3d cell range reference
5386 1
                case 'tRefN':
5387 1
                case 'tAreaN':
5388 1
                case 'tStr': // string
5389 2
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5390 2
                    unset($space0, $space1);
5391 2
                    break;
5392
            }
5393
        }
5394 2
        $formulaString = $formulaStrings[0];
5395
5396 2
        return $formulaString;
5397
    }
5398
5399
    /**
5400
     * Fetch next token from binary formula data.
5401
     *
5402
     * @param string $formulaData Formula data
5403
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5404
     *
5405
     * @throws Exception
5406
     *
5407
     * @return array
5408
     */
5409 2
    private function getNextToken($formulaData, $baseCell = 'A1')
5410
    {
5411
        // offset: 0; size: 1; token id
5412 2
        $id = ord($formulaData[0]); // token id
5413 2
        $name = false; // initialize token name
0 ignored issues
show
Unused Code introduced by
$name 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...
5414
5415
        switch ($id) {
5416 2
            case 0x03:
5417 1
                $name = 'tAdd';
5418 1
                $size = 1;
5419 1
                $data = '+';
5420 1
                break;
5421 2
            case 0x04:
5422
                $name = 'tSub';
5423
                $size = 1;
5424
                $data = '-';
5425
                break;
5426 2
            case 0x05:
5427 2
                $name = 'tMul';
5428 2
                $size = 1;
5429 2
                $data = '*';
5430 2
                break;
5431 2
            case 0x06:
5432
                $name = 'tDiv';
5433
                $size = 1;
5434
                $data = '/';
5435
                break;
5436 2
            case 0x07:
5437
                $name = 'tPower';
5438
                $size = 1;
5439
                $data = '^';
5440
                break;
5441 2
            case 0x08:
5442
                $name = 'tConcat';
5443
                $size = 1;
5444
                $data = '&';
5445
                break;
5446 2
            case 0x09:
5447
                $name = 'tLT';
5448
                $size = 1;
5449
                $data = '<';
5450
                break;
5451 2
            case 0x0A:
5452
                $name = 'tLE';
5453
                $size = 1;
5454
                $data = '<=';
5455
                break;
5456 2
            case 0x0B:
5457
                $name = 'tEQ';
5458
                $size = 1;
5459
                $data = '=';
5460
                break;
5461 2
            case 0x0C:
5462
                $name = 'tGE';
5463
                $size = 1;
5464
                $data = '>=';
5465
                break;
5466 2
            case 0x0D:
5467
                $name = 'tGT';
5468
                $size = 1;
5469
                $data = '>';
5470
                break;
5471 2
            case 0x0E:
5472 1
                $name = 'tNE';
5473 1
                $size = 1;
5474 1
                $data = '<>';
5475 1
                break;
5476 2
            case 0x0F:
5477
                $name = 'tIsect';
5478
                $size = 1;
5479
                $data = ' ';
5480
                break;
5481 2
            case 0x10:
5482
                $name = 'tList';
5483
                $size = 1;
5484
                $data = ',';
5485
                break;
5486 2
            case 0x11:
5487
                $name = 'tRange';
5488
                $size = 1;
5489
                $data = ':';
5490
                break;
5491 2
            case 0x12:
5492
                $name = 'tUplus';
5493
                $size = 1;
5494
                $data = '+';
5495
                break;
5496 2
            case 0x13:
5497
                $name = 'tUminus';
5498
                $size = 1;
5499
                $data = '-';
5500
                break;
5501 2
            case 0x14:
5502
                $name = 'tPercent';
5503
                $size = 1;
5504
                $data = '%';
5505
                break;
5506 2
            case 0x15:    //    parenthesis
5507
                $name = 'tParen';
5508
                $size = 1;
5509
                $data = null;
5510
                break;
5511 2
            case 0x16:    //    missing argument
5512
                $name = 'tMissArg';
5513
                $size = 1;
5514
                $data = '';
5515
                break;
5516 2
            case 0x17:    //    string
5517 1
                $name = 'tStr';
5518
                // offset: 1; size: var; Unicode string, 8-bit string length
5519 1
                $string = self::readUnicodeStringShort(substr($formulaData, 1));
5520 1
                $size = 1 + $string['size'];
5521 1
                $data = self::UTF8toExcelDoubleQuoted($string['value']);
5522 1
                break;
5523 2
            case 0x19:    //    Special attribute
5524
                // offset: 1; size: 1; attribute type flags:
5525 1
                switch (ord($formulaData[1])) {
5526 1
                    case 0x01:
5527
                        $name = 'tAttrVolatile';
5528
                        $size = 4;
5529
                        $data = null;
5530
                        break;
5531 1
                    case 0x02:
5532
                        $name = 'tAttrIf';
5533
                        $size = 4;
5534
                        $data = null;
5535
                        break;
5536 1
                    case 0x04:
5537
                        $name = 'tAttrChoose';
5538
                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
5539
                        $nc = self::getInt2d($formulaData, 2);
5540
                        // offset: 4; size: 2 * $nc
5541
                        // offset: 4 + 2 * $nc; size: 2
5542
                        $size = 2 * $nc + 6;
5543
                        $data = null;
5544
                        break;
5545 1
                    case 0x08:
5546
                        $name = 'tAttrSkip';
5547
                        $size = 4;
5548
                        $data = null;
5549
                        break;
5550 1
                    case 0x10:
5551 1
                        $name = 'tAttrSum';
5552 1
                        $size = 4;
5553 1
                        $data = null;
5554 1
                        break;
5555
                    case 0x40:
5556
                    case 0x41:
5557
                        $name = 'tAttrSpace';
5558
                        $size = 4;
5559
                        // offset: 2; size: 2; space type and position
5560
                        switch (ord($formulaData[2])) {
5561
                            case 0x00:
5562
                                $spacetype = 'type0';
5563
                                break;
5564
                            case 0x01:
5565
                                $spacetype = 'type1';
5566
                                break;
5567
                            case 0x02:
5568
                                $spacetype = 'type2';
5569
                                break;
5570
                            case 0x03:
5571
                                $spacetype = 'type3';
5572
                                break;
5573
                            case 0x04:
5574
                                $spacetype = 'type4';
5575
                                break;
5576
                            case 0x05:
5577
                                $spacetype = 'type5';
5578
                                break;
5579
                            default:
5580
                                throw new Exception('Unrecognized space type in tAttrSpace token');
5581
                                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5582
                        }
5583
                        // offset: 3; size: 1; number of inserted spaces/carriage returns
5584
                        $spacecount = ord($formulaData[3]);
5585
5586
                        $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
5587
                        break;
5588
                    default:
5589
                        throw new Exception('Unrecognized attribute flag in tAttr token');
5590
                        break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5591
                }
5592 1
                break;
5593 2
            case 0x1C:    //    error code
5594
                // offset: 1; size: 1; error code
5595
                $name = 'tErr';
5596
                $size = 2;
5597
                $data = Xls\ErrorCode::lookup(ord($formulaData[1]));
5598
                break;
5599 2
            case 0x1D:    //    boolean
5600
                // offset: 1; size: 1; 0 = false, 1 = true;
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
5601
                $name = 'tBool';
5602
                $size = 2;
5603
                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
5604
                break;
5605 2
            case 0x1E:    //    integer
5606
                // offset: 1; size: 2; unsigned 16-bit integer
5607
                $name = 'tInt';
5608
                $size = 3;
5609
                $data = self::getInt2d($formulaData, 1);
5610
                break;
5611 2
            case 0x1F:    //    number
5612
                // offset: 1; size: 8;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
5613 2
                $name = 'tNum';
5614 2
                $size = 9;
5615 2
                $data = self::extractNumber(substr($formulaData, 1));
5616 2
                $data = str_replace(',', '.', (string) $data); // in case non-English locale
5617 2
                break;
5618 2
            case 0x20:    //    array constant
5619 2
            case 0x40:
5620 2
            case 0x60:
5621
                // offset: 1; size: 7; not used
5622
                $name = 'tArray';
5623
                $size = 8;
5624
                $data = null;
5625
                break;
5626 2
            case 0x21:    //    function with fixed number of arguments
5627 2
            case 0x41:
5628 2
            case 0x61:
5629
                $name = 'tFunc';
5630
                $size = 3;
5631
                // offset: 1; size: 2; index to built-in sheet function
5632
                switch (self::getInt2d($formulaData, 1)) {
5633
                    case 2:
5634
                        $function = 'ISNA';
5635
                        $args = 1;
5636
                        break;
5637
                    case 3:
5638
                        $function = 'ISERROR';
5639
                        $args = 1;
5640
                        break;
5641
                    case 10:
5642
                        $function = 'NA';
5643
                        $args = 0;
5644
                        break;
5645
                    case 15:
5646
                        $function = 'SIN';
5647
                        $args = 1;
5648
                        break;
5649
                    case 16:
5650
                        $function = 'COS';
5651
                        $args = 1;
5652
                        break;
5653
                    case 17:
5654
                        $function = 'TAN';
5655
                        $args = 1;
5656
                        break;
5657
                    case 18:
5658
                        $function = 'ATAN';
5659
                        $args = 1;
5660
                        break;
5661
                    case 19:
5662
                        $function = 'PI';
5663
                        $args = 0;
5664
                        break;
5665
                    case 20:
5666
                        $function = 'SQRT';
5667
                        $args = 1;
5668
                        break;
5669
                    case 21:
5670
                        $function = 'EXP';
5671
                        $args = 1;
5672
                        break;
5673
                    case 22:
5674
                        $function = 'LN';
5675
                        $args = 1;
5676
                        break;
5677
                    case 23:
5678
                        $function = 'LOG10';
5679
                        $args = 1;
5680
                        break;
5681
                    case 24:
5682
                        $function = 'ABS';
5683
                        $args = 1;
5684
                        break;
5685
                    case 25:
5686
                        $function = 'INT';
5687
                        $args = 1;
5688
                        break;
5689
                    case 26:
5690
                        $function = 'SIGN';
5691
                        $args = 1;
5692
                        break;
5693
                    case 27:
5694
                        $function = 'ROUND';
5695
                        $args = 2;
5696
                        break;
5697
                    case 30:
5698
                        $function = 'REPT';
5699
                        $args = 2;
5700
                        break;
5701
                    case 31:
5702
                        $function = 'MID';
5703
                        $args = 3;
5704
                        break;
5705
                    case 32:
5706
                        $function = 'LEN';
5707
                        $args = 1;
5708
                        break;
5709
                    case 33:
5710
                        $function = 'VALUE';
5711
                        $args = 1;
5712
                        break;
5713
                    case 34:
5714
                        $function = 'TRUE';
5715
                        $args = 0;
5716
                        break;
5717
                    case 35:
5718
                        $function = 'FALSE';
5719
                        $args = 0;
5720
                        break;
5721
                    case 38:
5722
                        $function = 'NOT';
5723
                        $args = 1;
5724
                        break;
5725
                    case 39:
5726
                        $function = 'MOD';
5727
                        $args = 2;
5728
                        break;
5729
                    case 40:
5730
                        $function = 'DCOUNT';
5731
                        $args = 3;
5732
                        break;
5733
                    case 41:
5734
                        $function = 'DSUM';
5735
                        $args = 3;
5736
                        break;
5737
                    case 42:
5738
                        $function = 'DAVERAGE';
5739
                        $args = 3;
5740
                        break;
5741
                    case 43:
5742
                        $function = 'DMIN';
5743
                        $args = 3;
5744
                        break;
5745
                    case 44:
5746
                        $function = 'DMAX';
5747
                        $args = 3;
5748
                        break;
5749
                    case 45:
5750
                        $function = 'DSTDEV';
5751
                        $args = 3;
5752
                        break;
5753
                    case 48:
5754
                        $function = 'TEXT';
5755
                        $args = 2;
5756
                        break;
5757
                    case 61:
5758
                        $function = 'MIRR';
5759
                        $args = 3;
5760
                        break;
5761
                    case 63:
5762
                        $function = 'RAND';
5763
                        $args = 0;
5764
                        break;
5765
                    case 65:
5766
                        $function = 'DATE';
5767
                        $args = 3;
5768
                        break;
5769
                    case 66:
5770
                        $function = 'TIME';
5771
                        $args = 3;
5772
                        break;
5773
                    case 67:
5774
                        $function = 'DAY';
5775
                        $args = 1;
5776
                        break;
5777
                    case 68:
5778
                        $function = 'MONTH';
5779
                        $args = 1;
5780
                        break;
5781
                    case 69:
5782
                        $function = 'YEAR';
5783
                        $args = 1;
5784
                        break;
5785
                    case 71:
5786
                        $function = 'HOUR';
5787
                        $args = 1;
5788
                        break;
5789
                    case 72:
5790
                        $function = 'MINUTE';
5791
                        $args = 1;
5792
                        break;
5793
                    case 73:
5794
                        $function = 'SECOND';
5795
                        $args = 1;
5796
                        break;
5797
                    case 74:
5798
                        $function = 'NOW';
5799
                        $args = 0;
5800
                        break;
5801
                    case 75:
5802
                        $function = 'AREAS';
5803
                        $args = 1;
5804
                        break;
5805
                    case 76:
5806
                        $function = 'ROWS';
5807
                        $args = 1;
5808
                        break;
5809
                    case 77:
5810
                        $function = 'COLUMNS';
5811
                        $args = 1;
5812
                        break;
5813
                    case 83:
5814
                        $function = 'TRANSPOSE';
5815
                        $args = 1;
5816
                        break;
5817
                    case 86:
5818
                        $function = 'TYPE';
5819
                        $args = 1;
5820
                        break;
5821
                    case 97:
5822
                        $function = 'ATAN2';
5823
                        $args = 2;
5824
                        break;
5825
                    case 98:
5826
                        $function = 'ASIN';
5827
                        $args = 1;
5828
                        break;
5829
                    case 99:
5830
                        $function = 'ACOS';
5831
                        $args = 1;
5832
                        break;
5833
                    case 105:
5834
                        $function = 'ISREF';
5835
                        $args = 1;
5836
                        break;
5837
                    case 111:
5838
                        $function = 'CHAR';
5839
                        $args = 1;
5840
                        break;
5841
                    case 112:
5842
                        $function = 'LOWER';
5843
                        $args = 1;
5844
                        break;
5845
                    case 113:
5846
                        $function = 'UPPER';
5847
                        $args = 1;
5848
                        break;
5849
                    case 114:
5850
                        $function = 'PROPER';
5851
                        $args = 1;
5852
                        break;
5853
                    case 117:
5854
                        $function = 'EXACT';
5855
                        $args = 2;
5856
                        break;
5857
                    case 118:
5858
                        $function = 'TRIM';
5859
                        $args = 1;
5860
                        break;
5861
                    case 119:
5862
                        $function = 'REPLACE';
5863
                        $args = 4;
5864
                        break;
5865
                    case 121:
5866
                        $function = 'CODE';
5867
                        $args = 1;
5868
                        break;
5869
                    case 126:
5870
                        $function = 'ISERR';
5871
                        $args = 1;
5872
                        break;
5873
                    case 127:
5874
                        $function = 'ISTEXT';
5875
                        $args = 1;
5876
                        break;
5877
                    case 128:
5878
                        $function = 'ISNUMBER';
5879
                        $args = 1;
5880
                        break;
5881
                    case 129:
5882
                        $function = 'ISBLANK';
5883
                        $args = 1;
5884
                        break;
5885
                    case 130:
5886
                        $function = 'T';
5887
                        $args = 1;
5888
                        break;
5889
                    case 131:
5890
                        $function = 'N';
5891
                        $args = 1;
5892
                        break;
5893
                    case 140:
5894
                        $function = 'DATEVALUE';
5895
                        $args = 1;
5896
                        break;
5897
                    case 141:
5898
                        $function = 'TIMEVALUE';
5899
                        $args = 1;
5900
                        break;
5901
                    case 142:
5902
                        $function = 'SLN';
5903
                        $args = 3;
5904
                        break;
5905
                    case 143:
5906
                        $function = 'SYD';
5907
                        $args = 4;
5908
                        break;
5909
                    case 162:
5910
                        $function = 'CLEAN';
5911
                        $args = 1;
5912
                        break;
5913
                    case 163:
5914
                        $function = 'MDETERM';
5915
                        $args = 1;
5916
                        break;
5917
                    case 164:
5918
                        $function = 'MINVERSE';
5919
                        $args = 1;
5920
                        break;
5921
                    case 165:
5922
                        $function = 'MMULT';
5923
                        $args = 2;
5924
                        break;
5925
                    case 184:
5926
                        $function = 'FACT';
5927
                        $args = 1;
5928
                        break;
5929
                    case 189:
5930
                        $function = 'DPRODUCT';
5931
                        $args = 3;
5932
                        break;
5933
                    case 190:
5934
                        $function = 'ISNONTEXT';
5935
                        $args = 1;
5936
                        break;
5937
                    case 195:
5938
                        $function = 'DSTDEVP';
5939
                        $args = 3;
5940
                        break;
5941
                    case 196:
5942
                        $function = 'DVARP';
5943
                        $args = 3;
5944
                        break;
5945
                    case 198:
5946
                        $function = 'ISLOGICAL';
5947
                        $args = 1;
5948
                        break;
5949
                    case 199:
5950
                        $function = 'DCOUNTA';
5951
                        $args = 3;
5952
                        break;
5953
                    case 207:
5954
                        $function = 'REPLACEB';
5955
                        $args = 4;
5956
                        break;
5957
                    case 210:
5958
                        $function = 'MIDB';
5959
                        $args = 3;
5960
                        break;
5961
                    case 211:
5962
                        $function = 'LENB';
5963
                        $args = 1;
5964
                        break;
5965
                    case 212:
5966
                        $function = 'ROUNDUP';
5967
                        $args = 2;
5968
                        break;
5969
                    case 213:
5970
                        $function = 'ROUNDDOWN';
5971
                        $args = 2;
5972
                        break;
5973
                    case 214:
5974
                        $function = 'ASC';
5975
                        $args = 1;
5976
                        break;
5977
                    case 215:
5978
                        $function = 'DBCS';
5979
                        $args = 1;
5980
                        break;
5981
                    case 221:
5982
                        $function = 'TODAY';
5983
                        $args = 0;
5984
                        break;
5985
                    case 229:
5986
                        $function = 'SINH';
5987
                        $args = 1;
5988
                        break;
5989
                    case 230:
5990
                        $function = 'COSH';
5991
                        $args = 1;
5992
                        break;
5993
                    case 231:
5994
                        $function = 'TANH';
5995
                        $args = 1;
5996
                        break;
5997
                    case 232:
5998
                        $function = 'ASINH';
5999
                        $args = 1;
6000
                        break;
6001
                    case 233:
6002
                        $function = 'ACOSH';
6003
                        $args = 1;
6004
                        break;
6005
                    case 234:
6006
                        $function = 'ATANH';
6007
                        $args = 1;
6008
                        break;
6009
                    case 235:
6010
                        $function = 'DGET';
6011
                        $args = 3;
6012
                        break;
6013
                    case 244:
6014
                        $function = 'INFO';
6015
                        $args = 1;
6016
                        break;
6017
                    case 252:
6018
                        $function = 'FREQUENCY';
6019
                        $args = 2;
6020
                        break;
6021
                    case 261:
6022
                        $function = 'ERROR.TYPE';
6023
                        $args = 1;
6024
                        break;
6025
                    case 271:
6026
                        $function = 'GAMMALN';
6027
                        $args = 1;
6028
                        break;
6029
                    case 273:
6030
                        $function = 'BINOMDIST';
6031
                        $args = 4;
6032
                        break;
6033
                    case 274:
6034
                        $function = 'CHIDIST';
6035
                        $args = 2;
6036
                        break;
6037
                    case 275:
6038
                        $function = 'CHIINV';
6039
                        $args = 2;
6040
                        break;
6041
                    case 276:
6042
                        $function = 'COMBIN';
6043
                        $args = 2;
6044
                        break;
6045
                    case 277:
6046
                        $function = 'CONFIDENCE';
6047
                        $args = 3;
6048
                        break;
6049
                    case 278:
6050
                        $function = 'CRITBINOM';
6051
                        $args = 3;
6052
                        break;
6053
                    case 279:
6054
                        $function = 'EVEN';
6055
                        $args = 1;
6056
                        break;
6057
                    case 280:
6058
                        $function = 'EXPONDIST';
6059
                        $args = 3;
6060
                        break;
6061
                    case 281:
6062
                        $function = 'FDIST';
6063
                        $args = 3;
6064
                        break;
6065
                    case 282:
6066
                        $function = 'FINV';
6067
                        $args = 3;
6068
                        break;
6069
                    case 283:
6070
                        $function = 'FISHER';
6071
                        $args = 1;
6072
                        break;
6073
                    case 284:
6074
                        $function = 'FISHERINV';
6075
                        $args = 1;
6076
                        break;
6077
                    case 285:
6078
                        $function = 'FLOOR';
6079
                        $args = 2;
6080
                        break;
6081
                    case 286:
6082
                        $function = 'GAMMADIST';
6083
                        $args = 4;
6084
                        break;
6085
                    case 287:
6086
                        $function = 'GAMMAINV';
6087
                        $args = 3;
6088
                        break;
6089
                    case 288:
6090
                        $function = 'CEILING';
6091
                        $args = 2;
6092
                        break;
6093
                    case 289:
6094
                        $function = 'HYPGEOMDIST';
6095
                        $args = 4;
6096
                        break;
6097
                    case 290:
6098
                        $function = 'LOGNORMDIST';
6099
                        $args = 3;
6100
                        break;
6101
                    case 291:
6102
                        $function = 'LOGINV';
6103
                        $args = 3;
6104
                        break;
6105
                    case 292:
6106
                        $function = 'NEGBINOMDIST';
6107
                        $args = 3;
6108
                        break;
6109
                    case 293:
6110
                        $function = 'NORMDIST';
6111
                        $args = 4;
6112
                        break;
6113
                    case 294:
6114
                        $function = 'NORMSDIST';
6115
                        $args = 1;
6116
                        break;
6117
                    case 295:
6118
                        $function = 'NORMINV';
6119
                        $args = 3;
6120
                        break;
6121
                    case 296:
6122
                        $function = 'NORMSINV';
6123
                        $args = 1;
6124
                        break;
6125
                    case 297:
6126
                        $function = 'STANDARDIZE';
6127
                        $args = 3;
6128
                        break;
6129
                    case 298:
6130
                        $function = 'ODD';
6131
                        $args = 1;
6132
                        break;
6133
                    case 299:
6134
                        $function = 'PERMUT';
6135
                        $args = 2;
6136
                        break;
6137
                    case 300:
6138
                        $function = 'POISSON';
6139
                        $args = 3;
6140
                        break;
6141
                    case 301:
6142
                        $function = 'TDIST';
6143
                        $args = 3;
6144
                        break;
6145
                    case 302:
6146
                        $function = 'WEIBULL';
6147
                        $args = 4;
6148
                        break;
6149
                    case 303:
6150
                        $function = 'SUMXMY2';
6151
                        $args = 2;
6152
                        break;
6153
                    case 304:
6154
                        $function = 'SUMX2MY2';
6155
                        $args = 2;
6156
                        break;
6157
                    case 305:
6158
                        $function = 'SUMX2PY2';
6159
                        $args = 2;
6160
                        break;
6161
                    case 306:
6162
                        $function = 'CHITEST';
6163
                        $args = 2;
6164
                        break;
6165
                    case 307:
6166
                        $function = 'CORREL';
6167
                        $args = 2;
6168
                        break;
6169
                    case 308:
6170
                        $function = 'COVAR';
6171
                        $args = 2;
6172
                        break;
6173
                    case 309:
6174
                        $function = 'FORECAST';
6175
                        $args = 3;
6176
                        break;
6177
                    case 310:
6178
                        $function = 'FTEST';
6179
                        $args = 2;
6180
                        break;
6181
                    case 311:
6182
                        $function = 'INTERCEPT';
6183
                        $args = 2;
6184
                        break;
6185
                    case 312:
6186
                        $function = 'PEARSON';
6187
                        $args = 2;
6188
                        break;
6189
                    case 313:
6190
                        $function = 'RSQ';
6191
                        $args = 2;
6192
                        break;
6193
                    case 314:
6194
                        $function = 'STEYX';
6195
                        $args = 2;
6196
                        break;
6197
                    case 315:
6198
                        $function = 'SLOPE';
6199
                        $args = 2;
6200
                        break;
6201
                    case 316:
6202
                        $function = 'TTEST';
6203
                        $args = 4;
6204
                        break;
6205
                    case 325:
6206
                        $function = 'LARGE';
6207
                        $args = 2;
6208
                        break;
6209
                    case 326:
6210
                        $function = 'SMALL';
6211
                        $args = 2;
6212
                        break;
6213
                    case 327:
6214
                        $function = 'QUARTILE';
6215
                        $args = 2;
6216
                        break;
6217
                    case 328:
6218
                        $function = 'PERCENTILE';
6219
                        $args = 2;
6220
                        break;
6221
                    case 331:
6222
                        $function = 'TRIMMEAN';
6223
                        $args = 2;
6224
                        break;
6225
                    case 332:
6226
                        $function = 'TINV';
6227
                        $args = 2;
6228
                        break;
6229
                    case 337:
6230
                        $function = 'POWER';
6231
                        $args = 2;
6232
                        break;
6233
                    case 342:
6234
                        $function = 'RADIANS';
6235
                        $args = 1;
6236
                        break;
6237
                    case 343:
6238
                        $function = 'DEGREES';
6239
                        $args = 1;
6240
                        break;
6241
                    case 346:
6242
                        $function = 'COUNTIF';
6243
                        $args = 2;
6244
                        break;
6245
                    case 347:
6246
                        $function = 'COUNTBLANK';
6247
                        $args = 1;
6248
                        break;
6249
                    case 350:
6250
                        $function = 'ISPMT';
6251
                        $args = 4;
6252
                        break;
6253
                    case 351:
6254
                        $function = 'DATEDIF';
6255
                        $args = 3;
6256
                        break;
6257
                    case 352:
6258
                        $function = 'DATESTRING';
6259
                        $args = 1;
6260
                        break;
6261
                    case 353:
6262
                        $function = 'NUMBERSTRING';
6263
                        $args = 2;
6264
                        break;
6265
                    case 360:
6266
                        $function = 'PHONETIC';
6267
                        $args = 1;
6268
                        break;
6269
                    case 368:
6270
                        $function = 'BAHTTEXT';
6271
                        $args = 1;
6272
                        break;
6273
                    default:
6274
                        throw new Exception('Unrecognized function in formula');
6275
                        break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
6276
                }
6277
                $data = ['function' => $function, 'args' => $args];
6278
                break;
6279 2
            case 0x22:    //    function with variable number of arguments
6280 2
            case 0x42:
6281 2
            case 0x62:
6282 1
                $name = 'tFuncV';
6283 1
                $size = 4;
6284
                // offset: 1; size: 1; number of arguments
6285 1
                $args = ord($formulaData[1]);
6286
                // offset: 2: size: 2; index to built-in sheet function
6287 1
                $index = self::getInt2d($formulaData, 2);
6288
                switch ($index) {
6289 1
                    case 0:
6290
                        $function = 'COUNT';
6291
                        break;
6292 1
                    case 1:
6293 1
                        $function = 'IF';
6294 1
                        break;
6295 1
                    case 4:
6296 1
                        $function = 'SUM';
6297 1
                        break;
6298
                    case 5:
6299
                        $function = 'AVERAGE';
6300
                        break;
6301
                    case 6:
6302
                        $function = 'MIN';
6303
                        break;
6304
                    case 7:
6305
                        $function = 'MAX';
6306
                        break;
6307
                    case 8:
6308
                        $function = 'ROW';
6309
                        break;
6310
                    case 9:
6311
                        $function = 'COLUMN';
6312
                        break;
6313
                    case 11:
6314
                        $function = 'NPV';
6315
                        break;
6316
                    case 12:
6317
                        $function = 'STDEV';
6318
                        break;
6319
                    case 13:
6320
                        $function = 'DOLLAR';
6321
                        break;
6322
                    case 14:
6323
                        $function = 'FIXED';
6324
                        break;
6325
                    case 28:
6326
                        $function = 'LOOKUP';
6327
                        break;
6328
                    case 29:
6329
                        $function = 'INDEX';
6330
                        break;
6331
                    case 36:
6332
                        $function = 'AND';
6333
                        break;
6334
                    case 37:
6335
                        $function = 'OR';
6336
                        break;
6337
                    case 46:
6338
                        $function = 'VAR';
6339
                        break;
6340
                    case 49:
6341
                        $function = 'LINEST';
6342
                        break;
6343
                    case 50:
6344
                        $function = 'TREND';
6345
                        break;
6346
                    case 51:
6347
                        $function = 'LOGEST';
6348
                        break;
6349
                    case 52:
6350
                        $function = 'GROWTH';
6351
                        break;
6352
                    case 56:
6353
                        $function = 'PV';
6354
                        break;
6355
                    case 57:
6356
                        $function = 'FV';
6357
                        break;
6358
                    case 58:
6359
                        $function = 'NPER';
6360
                        break;
6361
                    case 59:
6362
                        $function = 'PMT';
6363
                        break;
6364
                    case 60:
6365
                        $function = 'RATE';
6366
                        break;
6367
                    case 62:
6368
                        $function = 'IRR';
6369
                        break;
6370
                    case 64:
6371
                        $function = 'MATCH';
6372
                        break;
6373
                    case 70:
6374
                        $function = 'WEEKDAY';
6375
                        break;
6376
                    case 78:
6377
                        $function = 'OFFSET';
6378
                        break;
6379
                    case 82:
6380
                        $function = 'SEARCH';
6381
                        break;
6382
                    case 100:
6383
                        $function = 'CHOOSE';
6384
                        break;
6385
                    case 101:
6386
                        $function = 'HLOOKUP';
6387
                        break;
6388
                    case 102:
6389
                        $function = 'VLOOKUP';
6390
                        break;
6391
                    case 109:
6392
                        $function = 'LOG';
6393
                        break;
6394
                    case 115:
6395
                        $function = 'LEFT';
6396
                        break;
6397
                    case 116:
6398
                        $function = 'RIGHT';
6399
                        break;
6400
                    case 120:
6401
                        $function = 'SUBSTITUTE';
6402
                        break;
6403
                    case 124:
6404
                        $function = 'FIND';
6405
                        break;
6406
                    case 125:
6407
                        $function = 'CELL';
6408
                        break;
6409
                    case 144:
6410
                        $function = 'DDB';
6411
                        break;
6412
                    case 148:
6413
                        $function = 'INDIRECT';
6414
                        break;
6415
                    case 167:
6416
                        $function = 'IPMT';
6417
                        break;
6418
                    case 168:
6419
                        $function = 'PPMT';
6420
                        break;
6421
                    case 169:
6422
                        $function = 'COUNTA';
6423
                        break;
6424
                    case 183:
6425
                        $function = 'PRODUCT';
6426
                        break;
6427
                    case 193:
6428
                        $function = 'STDEVP';
6429
                        break;
6430
                    case 194:
6431
                        $function = 'VARP';
6432
                        break;
6433
                    case 197:
6434
                        $function = 'TRUNC';
6435
                        break;
6436
                    case 204:
6437
                        $function = 'USDOLLAR';
6438
                        break;
6439
                    case 205:
6440
                        $function = 'FINDB';
6441
                        break;
6442
                    case 206:
6443
                        $function = 'SEARCHB';
6444
                        break;
6445
                    case 208:
6446
                        $function = 'LEFTB';
6447
                        break;
6448
                    case 209:
6449
                        $function = 'RIGHTB';
6450
                        break;
6451
                    case 216:
6452
                        $function = 'RANK';
6453
                        break;
6454
                    case 219:
6455
                        $function = 'ADDRESS';
6456
                        break;
6457
                    case 220:
6458
                        $function = 'DAYS360';
6459
                        break;
6460
                    case 222:
6461
                        $function = 'VDB';
6462
                        break;
6463
                    case 227:
6464
                        $function = 'MEDIAN';
6465
                        break;
6466
                    case 228:
6467
                        $function = 'SUMPRODUCT';
6468
                        break;
6469
                    case 247:
6470
                        $function = 'DB';
6471
                        break;
6472
                    case 255:
6473
                        $function = '';
6474
                        break;
6475
                    case 269:
6476
                        $function = 'AVEDEV';
6477
                        break;
6478
                    case 270:
6479
                        $function = 'BETADIST';
6480
                        break;
6481
                    case 272:
6482
                        $function = 'BETAINV';
6483
                        break;
6484
                    case 317:
6485
                        $function = 'PROB';
6486
                        break;
6487
                    case 318:
6488
                        $function = 'DEVSQ';
6489
                        break;
6490
                    case 319:
6491
                        $function = 'GEOMEAN';
6492
                        break;
6493
                    case 320:
6494
                        $function = 'HARMEAN';
6495
                        break;
6496
                    case 321:
6497
                        $function = 'SUMSQ';
6498
                        break;
6499
                    case 322:
6500
                        $function = 'KURT';
6501
                        break;
6502
                    case 323:
6503
                        $function = 'SKEW';
6504
                        break;
6505
                    case 324:
6506
                        $function = 'ZTEST';
6507
                        break;
6508
                    case 329:
6509
                        $function = 'PERCENTRANK';
6510
                        break;
6511
                    case 330:
6512
                        $function = 'MODE';
6513
                        break;
6514
                    case 336:
6515
                        $function = 'CONCATENATE';
6516
                        break;
6517
                    case 344:
6518
                        $function = 'SUBTOTAL';
6519
                        break;
6520
                    case 345:
6521
                        $function = 'SUMIF';
6522
                        break;
6523
                    case 354:
6524
                        $function = 'ROMAN';
6525
                        break;
6526
                    case 358:
6527
                        $function = 'GETPIVOTDATA';
6528
                        break;
6529
                    case 359:
6530
                        $function = 'HYPERLINK';
6531
                        break;
6532
                    case 361:
6533
                        $function = 'AVERAGEA';
6534
                        break;
6535
                    case 362:
6536
                        $function = 'MAXA';
6537
                        break;
6538
                    case 363:
6539
                        $function = 'MINA';
6540
                        break;
6541
                    case 364:
6542
                        $function = 'STDEVPA';
6543
                        break;
6544
                    case 365:
6545
                        $function = 'VARPA';
6546
                        break;
6547
                    case 366:
6548
                        $function = 'STDEVA';
6549
                        break;
6550
                    case 367:
6551
                        $function = 'VARA';
6552
                        break;
6553
                    default:
6554
                        throw new Exception('Unrecognized function in formula');
6555
                        break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
6556
                }
6557 1
                $data = ['function' => $function, 'args' => $args];
6558 1
                break;
6559 2
            case 0x23:    //    index to defined name
6560 2
            case 0x43:
6561 2
            case 0x63:
6562
                $name = 'tName';
6563
                $size = 5;
6564
                // offset: 1; size: 2; one-based index to definedname record
6565
                $definedNameIndex = self::getInt2d($formulaData, 1) - 1;
6566
                // offset: 2; size: 2; not used
6567
                $data = $this->definedname[$definedNameIndex]['name'];
6568
                break;
6569 2
            case 0x24:    //    single cell reference e.g. A5
6570 2
            case 0x44:
6571 2
            case 0x64:
6572 2
                $name = 'tRef';
6573 2
                $size = 5;
6574 2
                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
6575 2
                break;
6576 2
            case 0x25:    //    cell range reference to cells in the same sheet (2d)
6577 1
            case 0x45:
6578 1
            case 0x65:
6579 2
                $name = 'tArea';
6580 2
                $size = 9;
6581 2
                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
6582 2
                break;
6583 1
            case 0x26:    //    Constant reference sub-expression
6584 1
            case 0x46:
6585 1 View Code Duplication
            case 0x66:
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...
6586
                $name = 'tMemArea';
6587
                // offset: 1; size: 4; not used
6588
                // offset: 5; size: 2; size of the following subexpression
6589
                $subSize = self::getInt2d($formulaData, 5);
6590
                $size = 7 + $subSize;
6591
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6592
                break;
6593 1
            case 0x27:    //    Deleted constant reference sub-expression
6594 1
            case 0x47:
6595 1 View Code Duplication
            case 0x67:
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...
6596
                $name = 'tMemErr';
6597
                // offset: 1; size: 4; not used
6598
                // offset: 5; size: 2; size of the following subexpression
6599
                $subSize = self::getInt2d($formulaData, 5);
6600
                $size = 7 + $subSize;
6601
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6602
                break;
6603 1
            case 0x29:    //    Variable reference sub-expression
6604 1
            case 0x49:
6605 1 View Code Duplication
            case 0x69:
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...
6606
                $name = 'tMemFunc';
6607
                // offset: 1; size: 2; size of the following sub-expression
6608
                $subSize = self::getInt2d($formulaData, 1);
6609
                $size = 3 + $subSize;
6610
                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
6611
                break;
6612 1
            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
6613 1
            case 0x4C:
6614 1
            case 0x6C:
6615
                $name = 'tRefN';
6616
                $size = 5;
6617
                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
6618
                break;
6619 1
            case 0x2D:    //    Relative 2d range reference
6620 1
            case 0x4D:
6621 1
            case 0x6D:
6622
                $name = 'tAreaN';
6623
                $size = 9;
6624
                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
6625
                break;
6626 1
            case 0x39:    //    External name
6627 1
            case 0x59:
6628 1
            case 0x79:
6629
                $name = 'tNameX';
6630
                $size = 7;
6631
                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
6632
                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
6633
                $index = self::getInt2d($formulaData, 3);
6634
                // assume index is to EXTERNNAME record
6635
                $data = $this->externalNames[$index - 1]['name'];
6636
                // offset: 5; size: 2; not used
6637
                break;
6638 1
            case 0x3A:    //    3d reference to cell
6639 1
            case 0x5A:
6640 1 View Code Duplication
            case 0x7A:
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...
6641
                $name = 'tRef3d';
6642
                $size = 7;
6643
6644
                try {
6645
                    // offset: 1; size: 2; index to REF entry
6646
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6647
                    // offset: 3; size: 4; cell address
6648
                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
6649
6650
                    $data = "$sheetRange!$cellAddress";
6651
                } catch (PhpSpreadsheetException $e) {
6652
                    // deleted sheet reference
6653
                    $data = '#REF!';
6654
                }
6655
                break;
6656 1
            case 0x3B:    //    3d reference to cell range
6657
            case 0x5B:
6658 View Code Duplication
            case 0x7B:
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...
6659 1
                $name = 'tArea3d';
6660 1
                $size = 11;
6661
6662
                try {
6663
                    // offset: 1; size: 2; index to REF entry
6664 1
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6665
                    // offset: 3; size: 8; cell address
6666 1
                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
6667
6668 1
                    $data = "$sheetRange!$cellRangeAddress";
6669
                } catch (PhpSpreadsheetException $e) {
6670
                    // deleted sheet reference
6671
                    $data = '#REF!';
6672
                }
6673 1
                break;
6674
            // Unknown cases    // don't know how to deal with
6675
            default:
6676
                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
6677
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
6678
        }
6679
6680
        return [
6681 2
            'id' => $id,
6682 2
            'name' => $name,
6683 2
            'size' => $size,
6684 2
            'data' => $data,
6685
        ];
6686
    }
6687
6688
    /**
6689
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
6690
     * section 3.3.4.
6691
     *
6692
     * @param string $cellAddressStructure
6693
     *
6694
     * @return string
6695
     */
6696 2
    private function readBIFF8CellAddress($cellAddressStructure)
6697
    {
6698
        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
6699 2
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6700
6701
        // offset: 2; size: 2; index to column or column offset + relative flags
6702
        // bit: 7-0; mask 0x00FF; column index
6703 2
        $column = Cell::stringFromColumnIndex(0x00FF & self::getInt2d($cellAddressStructure, 2));
6704
6705
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6706 2
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
6707 1
            $column = '$' . $column;
6708
        }
6709
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6710 2
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6711 1
            $row = '$' . $row;
6712
        }
6713
6714 2
        return $column . $row;
6715
    }
6716
6717
    /**
6718
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6719
     * to indicate offsets from a base cell
6720
     * section 3.3.4.
6721
     *
6722
     * @param string $cellAddressStructure
6723
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
6724
     *
6725
     * @return string
6726
     */
6727
    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
6728
    {
6729
        list($baseCol, $baseRow) = Cell::coordinateFromString($baseCell);
6730
        $baseCol = Cell::columnIndexFromString($baseCol) - 1;
6731
6732
        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
6733
        $rowIndex = self::getInt2d($cellAddressStructure, 0);
6734
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6735
6736
        // offset: 2; size: 2; index to column or column offset + relative flags
6737
        // bit: 7-0; mask 0x00FF; column index
6738
        $colIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
6739
6740
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6741 View Code Duplication
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
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...
6742
            $column = Cell::stringFromColumnIndex($colIndex);
6743
            $column = '$' . $column;
6744
        } else {
6745
            $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
6746
            $column = Cell::stringFromColumnIndex($baseCol + $colIndex);
6747
        }
6748
6749
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6750
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6751
            $row = '$' . $row;
6752
        } else {
6753
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
6754
            $row = $baseRow + $rowIndex;
6755
        }
6756
6757
        return $column . $row;
6758
    }
6759
6760
    /**
6761
     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
6762
     * always fixed range
6763
     * section 2.5.14.
6764
     *
6765
     * @param string $subData
6766
     *
6767
     * @throws Exception
6768
     *
6769
     * @return string
6770
     */
6771 4
    private function readBIFF5CellRangeAddressFixed($subData)
6772
    {
6773
        // offset: 0; size: 2; index to first row
6774 4
        $fr = self::getInt2d($subData, 0) + 1;
6775
6776
        // offset: 2; size: 2; index to last row
6777 4
        $lr = self::getInt2d($subData, 2) + 1;
6778
6779
        // offset: 4; size: 1; index to first column
6780 4
        $fc = ord($subData[4]);
6781
6782
        // offset: 5; size: 1; index to last column
6783 4
        $lc = ord($subData[5]);
6784
6785
        // check values
6786 4
        if ($fr > $lr || $fc > $lc) {
6787
            throw new Exception('Not a cell range address');
6788
        }
6789
6790
        // column index to letter
6791 4
        $fc = Cell::stringFromColumnIndex($fc);
6792 4
        $lc = Cell::stringFromColumnIndex($lc);
6793
6794 4
        if ($fr == $lr and $fc == $lc) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
6795 4
            return "$fc$fr";
6796
        }
6797
6798 1
        return "$fc$fr:$lc$lr";
6799
    }
6800
6801
    /**
6802
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
6803
     * always fixed range
6804
     * section 2.5.14.
6805
     *
6806
     * @param string $subData
6807
     *
6808
     * @throws Exception
6809
     *
6810
     * @return string
6811
     */
6812 2
    private function readBIFF8CellRangeAddressFixed($subData)
6813
    {
6814
        // offset: 0; size: 2; index to first row
6815 2
        $fr = self::getInt2d($subData, 0) + 1;
6816
6817
        // offset: 2; size: 2; index to last row
6818 2
        $lr = self::getInt2d($subData, 2) + 1;
6819
6820
        // offset: 4; size: 2; index to first column
6821 2
        $fc = self::getInt2d($subData, 4);
6822
6823
        // offset: 6; size: 2; index to last column
6824 2
        $lc = self::getInt2d($subData, 6);
6825
6826
        // check values
6827 2
        if ($fr > $lr || $fc > $lc) {
6828
            throw new Exception('Not a cell range address');
6829
        }
6830
6831
        // column index to letter
6832 2
        $fc = Cell::stringFromColumnIndex($fc);
6833 2
        $lc = Cell::stringFromColumnIndex($lc);
6834
6835 2
        if ($fr == $lr and $fc == $lc) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
6836 2
            return "$fc$fr";
6837
        }
6838
6839 2
        return "$fc$fr:$lc$lr";
6840
    }
6841
6842
    /**
6843
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
6844
     * there are flags indicating whether column/row index is relative
6845
     * section 3.3.4.
6846
     *
6847
     * @param string $subData
6848
     *
6849
     * @return string
6850
     */
6851 2
    private function readBIFF8CellRangeAddress($subData)
6852
    {
6853
        // todo: if cell range is just a single cell, should this funciton
6854
        // not just return e.g. 'A1' and not 'A1:A1' ?
6855
6856
        // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
6857 2
            $fr = self::getInt2d($subData, 0) + 1;
6858
6859
        // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
6860 2
            $lr = self::getInt2d($subData, 2) + 1;
6861
6862
        // offset: 4; size: 2; index to first column or column offset + relative flags
6863
6864
        // bit: 7-0; mask 0x00FF; column index
6865 2
        $fc = Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 4));
6866
6867
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6868 2
        if (!(0x4000 & self::getInt2d($subData, 4))) {
6869 1
            $fc = '$' . $fc;
6870
        }
6871
6872
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6873 2
        if (!(0x8000 & self::getInt2d($subData, 4))) {
6874 1
            $fr = '$' . $fr;
6875
        }
6876
6877
        // offset: 6; size: 2; index to last column or column offset + relative flags
6878
6879
        // bit: 7-0; mask 0x00FF; column index
6880 2
        $lc = Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 6));
6881
6882
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6883 2
        if (!(0x4000 & self::getInt2d($subData, 6))) {
6884 1
            $lc = '$' . $lc;
6885
        }
6886
6887
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6888 2
        if (!(0x8000 & self::getInt2d($subData, 6))) {
6889 1
            $lr = '$' . $lr;
6890
        }
6891
6892 2
        return "$fc$fr:$lc$lr";
6893
    }
6894
6895
    /**
6896
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6897
     * to indicate offsets from a base cell
6898
     * section 3.3.4.
6899
     *
6900
     * @param string $subData
6901
     * @param string $baseCell Base cell
6902
     *
6903
     * @return string Cell range address
6904
     */
6905
    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
6906
    {
6907
        list($baseCol, $baseRow) = Cell::coordinateFromString($baseCell);
6908
        $baseCol = Cell::columnIndexFromString($baseCol) - 1;
6909
6910
        // TODO: if cell range is just a single cell, should this funciton
6911
        // not just return e.g. 'A1' and not 'A1:A1' ?
6912
6913
        // offset: 0; size: 2; first row
6914
        $frIndex = self::getInt2d($subData, 0); // adjust below
6915
6916
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
6917
        $lrIndex = self::getInt2d($subData, 2); // adjust below
6918
6919
        // offset: 4; size: 2; first column with relative/absolute flags
6920
6921
        // bit: 7-0; mask 0x00FF; column index
6922
        $fcIndex = 0x00FF & self::getInt2d($subData, 4);
6923
6924
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6925 View Code Duplication
        if (!(0x4000 & self::getInt2d($subData, 4))) {
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...
6926
            // absolute column index
6927
            $fc = Cell::stringFromColumnIndex($fcIndex);
6928
            $fc = '$' . $fc;
6929
        } else {
6930
            // column offset
6931
            $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
6932
            $fc = Cell::stringFromColumnIndex($baseCol + $fcIndex);
6933
        }
6934
6935
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6936 View Code Duplication
        if (!(0x8000 & self::getInt2d($subData, 4))) {
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...
6937
            // absolute row index
6938
            $fr = $frIndex + 1;
6939
            $fr = '$' . $fr;
6940
        } else {
6941
            // row offset
6942
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
6943
            $fr = $baseRow + $frIndex;
6944
        }
6945
6946
        // offset: 6; size: 2; last column with relative/absolute flags
6947
6948
        // bit: 7-0; mask 0x00FF; column index
6949
        $lcIndex = 0x00FF & self::getInt2d($subData, 6);
6950
        $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6951
        $lc = Cell::stringFromColumnIndex($baseCol + $lcIndex);
0 ignored issues
show
Unused Code introduced by
$lc 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...
6952
6953
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6954 View Code Duplication
        if (!(0x4000 & self::getInt2d($subData, 6))) {
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...
6955
            // absolute column index
6956
            $lc = Cell::stringFromColumnIndex($lcIndex);
6957
            $lc = '$' . $lc;
6958
        } else {
6959
            // column offset
6960
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6961
            $lc = Cell::stringFromColumnIndex($baseCol + $lcIndex);
6962
        }
6963
6964
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6965 View Code Duplication
        if (!(0x8000 & self::getInt2d($subData, 6))) {
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...
6966
            // absolute row index
6967
            $lr = $lrIndex + 1;
6968
            $lr = '$' . $lr;
6969
        } else {
6970
            // row offset
6971
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
6972
            $lr = $baseRow + $lrIndex;
6973
        }
6974
6975
        return "$fc$fr:$lc$lr";
6976
    }
6977
6978
    /**
6979
     * Read BIFF8 cell range address list
6980
     * section 2.5.15.
6981
     *
6982
     * @param string $subData
6983
     *
6984
     * @return array
6985
     */
6986 2 View Code Duplication
    private function readBIFF8CellRangeAddressList($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
6987
    {
6988 2
        $cellRangeAddresses = [];
6989
6990
        // offset: 0; size: 2; number of the following cell range addresses
6991 2
        $nm = self::getInt2d($subData, 0);
6992
6993 2
        $offset = 2;
6994
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
6995 2
        for ($i = 0; $i < $nm; ++$i) {
6996 2
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
6997 2
            $offset += 8;
6998
        }
6999
7000
        return [
7001 2
            'size' => 2 + 8 * $nm,
7002 2
            'cellRangeAddresses' => $cellRangeAddresses,
7003
        ];
7004
    }
7005
7006
    /**
7007
     * Read BIFF5 cell range address list
7008
     * section 2.5.15.
7009
     *
7010
     * @param string $subData
7011
     *
7012
     * @return array
7013
     */
7014 4 View Code Duplication
    private function readBIFF5CellRangeAddressList($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
7015
    {
7016 4
        $cellRangeAddresses = [];
7017
7018
        // offset: 0; size: 2; number of the following cell range addresses
7019 4
        $nm = self::getInt2d($subData, 0);
7020
7021 4
        $offset = 2;
7022
        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
7023 4
        for ($i = 0; $i < $nm; ++$i) {
7024 4
            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
7025 4
            $offset += 6;
7026
        }
7027
7028
        return [
7029 4
            'size' => 2 + 6 * $nm,
7030 4
            'cellRangeAddresses' => $cellRangeAddresses,
7031
        ];
7032
    }
7033
7034
    /**
7035
     * Get a sheet range like Sheet1:Sheet3 from REF index
7036
     * Note: If there is only one sheet in the range, one gets e.g Sheet1
7037
     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
7038
     * in which case an Exception is thrown.
7039
     *
7040
     * @param int $index
7041
     *
7042
     * @throws Exception
7043
     *
7044
     * @return string|false
7045
     */
7046 1
    private function readSheetRangeByRefIndex($index)
7047
    {
7048 1
        if (isset($this->ref[$index])) {
7049 1
            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
7050
7051
            switch ($type) {
7052 1
                case 'internal':
7053
                    // check if we have a deleted 3d reference
7054 1
                    if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF or $this->ref[$index]['lastSheetIndex'] == 0xFFFF) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
7055
                        throw new Exception('Deleted sheet reference');
7056
                    }
7057
7058
                    // we have normal sheet range (collapsed or uncollapsed)
7059 1
                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
7060 1
                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
7061
7062 1
                    if ($firstSheetName == $lastSheetName) {
7063
                        // collapsed sheet range
7064 1
                        $sheetRange = $firstSheetName;
7065
                    } else {
7066
                        $sheetRange = "$firstSheetName:$lastSheetName";
7067
                    }
7068
7069
                    // escape the single-quotes
7070 1
                    $sheetRange = str_replace("'", "''", $sheetRange);
7071
7072
                    // if there are special characters, we need to enclose the range in single-quotes
7073
                    // todo: check if we have identified the whole set of special characters
7074
                    // it seems that the following characters are not accepted for sheet names
7075
                    // and we may assume that they are not present: []*/:\?
7076 1
                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
7077
                        $sheetRange = "'$sheetRange'";
7078
                    }
7079
7080 1
                    return $sheetRange;
7081
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
7082
                default:
7083
                    // TODO: external sheet support
7084
                    throw new Exception('Xls reader only supports internal sheets in fomulas');
7085
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
7086
            }
7087
        }
7088
7089
        return false;
7090
    }
7091
7092
    /**
7093
     * read BIFF8 constant value array from array data
7094
     * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
7095
     * section 2.5.8.
7096
     *
7097
     * @param string $arrayData
7098
     *
7099
     * @return array
7100
     */
7101
    private static function readBIFF8ConstantArray($arrayData)
7102
    {
7103
        // offset: 0; size: 1; number of columns decreased by 1
7104
        $nc = ord($arrayData[0]);
7105
7106
        // offset: 1; size: 2; number of rows decreased by 1
7107
        $nr = self::getInt2d($arrayData, 1);
7108
        $size = 3; // initialize
7109
        $arrayData = substr($arrayData, 3);
7110
7111
        // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
7112
        $matrixChunks = [];
7113
        for ($r = 1; $r <= $nr + 1; ++$r) {
7114
            $items = [];
7115
            for ($c = 1; $c <= $nc + 1; ++$c) {
7116
                $constant = self::readBIFF8Constant($arrayData);
7117
                $items[] = $constant['value'];
7118
                $arrayData = substr($arrayData, $constant['size']);
7119
                $size += $constant['size'];
7120
            }
7121
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
7122
        }
7123
        $matrix = '{' . implode(';', $matrixChunks) . '}';
7124
7125
        return [
7126
            'value' => $matrix,
7127
            'size' => $size,
7128
        ];
7129
    }
7130
7131
    /**
7132
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
7133
     * section 2.5.7
7134
     * returns e.g. array('value' => '5', 'size' => 9).
7135
     *
7136
     * @param string $valueData
7137
     *
7138
     * @return array
7139
     */
7140
    private static function readBIFF8Constant($valueData)
7141
    {
7142
        // offset: 0; size: 1; identifier for type of constant
7143
        $identifier = ord($valueData[0]);
7144
7145
        switch ($identifier) {
7146
            case 0x00: // empty constant (what is this?)
7147
                $value = '';
7148
                $size = 9;
7149
                break;
7150
            case 0x01: // number
7151
                // offset: 1; size: 8; IEEE 754 floating-point value
7152
                $value = self::extractNumber(substr($valueData, 1, 8));
7153
                $size = 9;
7154
                break;
7155
            case 0x02: // string value
7156
                // offset: 1; size: var; Unicode string, 16-bit string length
7157
                $string = self::readUnicodeStringLong(substr($valueData, 1));
7158
                $value = '"' . $string['value'] . '"';
7159
                $size = 1 + $string['size'];
7160
                break;
7161
            case 0x04: // boolean
7162
                // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
7163
                if (ord($valueData[1])) {
7164
                    $value = 'TRUE';
7165
                } else {
7166
                    $value = 'FALSE';
7167
                }
7168
                $size = 9;
7169
                break;
7170
            case 0x10: // error code
7171
                // offset: 1; size: 1; error code
7172
                $value = Xls\ErrorCode::lookup(ord($valueData[1]));
7173
                $size = 9;
7174
                break;
7175
        }
7176
7177
        return [
7178
            'value' => $value,
0 ignored issues
show
Bug introduced by
The variable $value 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...
7179
            'size' => $size,
0 ignored issues
show
Bug introduced by
The variable $size 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...
7180
        ];
7181
    }
7182
7183
    /**
7184
     * Extract RGB color
7185
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
7186
     *
7187
     * @param string $rgb Encoded RGB value (4 bytes)
7188
     *
7189
     * @return array
7190
     */
7191 3
    private static function readRGB($rgb)
7192
    {
7193
        // offset: 0; size 1; Red component
7194 3
        $r = ord($rgb[0]);
7195
7196
        // offset: 1; size: 1; Green component
7197 3
        $g = ord($rgb[1]);
7198
7199
        // offset: 2; size: 1; Blue component
7200 3
        $b = ord($rgb[2]);
7201
7202
        // HEX notation, e.g. 'FF00FC'
7203 3
        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
7204
7205 3
        return ['rgb' => $rgb];
7206
    }
7207
7208
    /**
7209
     * Read byte string (8-bit string length)
7210
     * OpenOffice documentation: 2.5.2.
7211
     *
7212
     * @param string $subData
7213
     *
7214
     * @return array
7215
     */
7216 View Code Duplication
    private function readByteStringShort($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
7217
    {
7218
        // offset: 0; size: 1; length of the string (character count)
7219
        $ln = ord($subData[0]);
7220
7221
        // offset: 1: size: var; character array (8-bit characters)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
7222
        $value = $this->decodeCodepage(substr($subData, 1, $ln));
7223
7224
        return [
7225
            'value' => $value,
7226
            'size' => 1 + $ln, // size in bytes of data structure
7227
        ];
7228
    }
7229
7230
    /**
7231
     * Read byte string (16-bit string length)
7232
     * OpenOffice documentation: 2.5.2.
7233
     *
7234
     * @param string $subData
7235
     *
7236
     * @return array
7237
     */
7238 View Code Duplication
    private function readByteStringLong($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
7239
    {
7240
        // offset: 0; size: 2; length of the string (character count)
7241
        $ln = self::getInt2d($subData, 0);
7242
7243
        // offset: 2: size: var; character array (8-bit characters)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
7244
        $value = $this->decodeCodepage(substr($subData, 2));
7245
7246
        //return $string;
7247
        return [
7248
            'value' => $value,
7249
            'size' => 2 + $ln, // size in bytes of data structure
7250
        ];
7251
    }
7252
7253
    /**
7254
     * Extracts an Excel Unicode short string (8-bit string length)
7255
     * OpenOffice documentation: 2.5.3
7256
     * function will automatically find out where the Unicode string ends.
7257
     *
7258
     * @param string $subData
7259
     *
7260
     * @return array
7261
     */
7262 4 View Code Duplication
    private static function readUnicodeStringShort($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
7263
    {
7264 4
        $value = '';
0 ignored issues
show
Unused Code introduced by
$value 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...
7265
7266
        // offset: 0: size: 1; length of the string (character count)
7267 4
        $characterCount = ord($subData[0]);
7268
7269 4
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7270
7271
        // add 1 for the string length
7272 4
        $string['size'] += 1;
7273
7274 4
        return $string;
7275
    }
7276
7277
    /**
7278
     * Extracts an Excel Unicode long string (16-bit string length)
7279
     * OpenOffice documentation: 2.5.3
7280
     * this function is under construction, needs to support rich text, and Asian phonetic settings.
7281
     *
7282
     * @param string $subData
7283
     *
7284
     * @return array
7285
     */
7286 4 View Code Duplication
    private static function readUnicodeStringLong($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
7287
    {
7288 4
        $value = '';
0 ignored issues
show
Unused Code introduced by
$value 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...
7289
7290
        // offset: 0: size: 2; length of the string (character count)
7291 4
        $characterCount = self::getInt2d($subData, 0);
7292
7293 4
        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7294
7295
        // add 2 for the string length
7296 4
        $string['size'] += 2;
7297
7298 4
        return $string;
7299
    }
7300
7301
    /**
7302
     * Read Unicode string with no string length field, but with known character count
7303
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7304
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
7305
     *
7306
     * @param string $subData
7307
     * @param int $characterCount
7308
     *
7309
     * @return array
7310
     */
7311 4
    private static function readUnicodeString($subData, $characterCount)
7312
    {
7313 4
        $value = '';
0 ignored issues
show
Unused Code introduced by
$value 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...
7314
7315
        // offset: 0: size: 1; option flags
7316
        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
7317 4
        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7318
7319
        // bit: 2; mask: 0x04; Asian phonetic settings
7320 4
        $hasAsian = (0x04) & ord($subData[0]) >> 2;
0 ignored issues
show
Unused Code introduced by
$hasAsian 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...
7321
7322
        // bit: 3; mask: 0x08; Rich-Text settings
7323 4
        $hasRichText = (0x08) & ord($subData[0]) >> 3;
0 ignored issues
show
Unused Code introduced by
$hasRichText 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...
7324
7325
        // offset: 1: size: var; character array
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
7326
        // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
7327
        // needs to be fixed
7328 4
        $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
7329
7330
        return [
7331 4
            'value' => $value,
7332 4
            'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
7333
        ];
7334
    }
7335
7336
    /**
7337
     * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
7338
     * Example:  hello"world  -->  "hello""world".
7339
     *
7340
     * @param string $value UTF-8 encoded string
7341
     *
7342
     * @return string
7343
     */
7344 1
    private static function UTF8toExcelDoubleQuoted($value)
7345
    {
7346 1
        return '"' . str_replace('"', '""', $value) . '"';
7347
    }
7348
7349
    /**
7350
     * Reads first 8 bytes of a string and return IEEE 754 float.
7351
     *
7352
     * @param string $data Binary string that is at least 8 bytes long
7353
     *
7354
     * @return float
7355
     */
7356 4
    private static function extractNumber($data)
7357
    {
7358 4
        $rknumhigh = self::getInt4d($data, 4);
7359 4
        $rknumlow = self::getInt4d($data, 0);
7360 4
        $sign = ($rknumhigh & 0x80000000) >> 31;
7361 4
        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
7362 4
        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
7363 4
        $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
7364 4
        $mantissalow2 = ($rknumlow & 0x7fffffff);
7365 4
        $value = $mantissa / pow(2, (20 - $exp));
7366
7367 4
        if ($mantissalow1 != 0) {
7368 2
            $value += 1 / pow(2, (21 - $exp));
7369
        }
7370
7371 4
        $value += $mantissalow2 / pow(2, (52 - $exp));
7372 4
        if ($sign) {
7373
            $value *= -1;
7374
        }
7375
7376 4
        return $value;
7377
    }
7378
7379
    /**
7380
     * @param int $rknum
7381
     */
7382 1
    private static function getIEEE754($rknum)
7383
    {
7384 1
        if (($rknum & 0x02) != 0) {
7385
            $value = $rknum >> 2;
7386
        } else {
7387
            // changes by mmp, info on IEEE754 encoding from
7388
            // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
7389
            // The RK format calls for using only the most significant 30 bits
7390
            // of the 64 bit floating point value. The other 34 bits are assumed
7391
            // to be 0 so we use the upper 30 bits of $rknum as follows...
7392 1
            $sign = ($rknum & 0x80000000) >> 31;
7393 1
            $exp = ($rknum & 0x7ff00000) >> 20;
7394 1
            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
7395 1
            $value = $mantissa / pow(2, (20 - ($exp - 1023)));
7396 1
            if ($sign) {
7397
                $value = -1 * $value;
7398
            }
7399
            //end of changes by mmp
7400
        }
7401 1
        if (($rknum & 0x01) != 0) {
7402
            $value /= 100;
7403
        }
7404
7405 1
        return $value;
7406
    }
7407
7408
    /**
7409
     * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
7410
     *
7411
     * @param string $string
7412
     * @param bool $compressed
7413
     *
7414
     * @return string
7415
     */
7416 4
    private static function encodeUTF16($string, $compressed = false)
7417
    {
7418 4
        if ($compressed) {
7419 3
            $string = self::uncompressByteString($string);
7420
        }
7421
7422 4
        return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
7423
    }
7424
7425
    /**
7426
     * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
7427
     *
7428
     * @param string $string
7429
     *
7430
     * @return string
7431
     */
7432 3
    private static function uncompressByteString($string)
7433
    {
7434 3
        $uncompressedString = '';
7435 3
        $strLen = strlen($string);
7436 3
        for ($i = 0; $i < $strLen; ++$i) {
7437 3
            $uncompressedString .= $string[$i] . "\0";
7438
        }
7439
7440 3
        return $uncompressedString;
7441
    }
7442
7443
    /**
7444
     * Convert string to UTF-8. Only used for BIFF5.
7445
     *
7446
     * @param string $string
7447
     *
7448
     * @return string
7449
     */
7450
    private function decodeCodepage($string)
7451
    {
7452
        return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
7453
    }
7454
7455
    /**
7456
     * Read 16-bit unsigned integer.
7457
     *
7458
     * @param string $data
7459
     * @param int $pos
7460
     *
7461
     * @return int
7462
     */
7463 4
    public static function getInt2d($data, $pos)
7464
    {
7465 4
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
7466
    }
7467
7468
    /**
7469
     * Read 32-bit signed integer.
7470
     *
7471
     * @param string $data
7472
     * @param int $pos
7473
     *
7474
     * @return int
7475
     */
7476 4
    public static function getInt4d($data, $pos)
7477
    {
7478
        // FIX: represent numbers correctly on 64-bit system
7479
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
7480
        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
7481 4
        $_or_24 = ord($data[$pos + 3]);
7482 4
        if ($_or_24 >= 128) {
7483
            // negative number
7484 2
            $_ord_24 = -abs((256 - $_or_24) << 24);
7485
        } else {
7486 4
            $_ord_24 = ($_or_24 & 127) << 24;
7487
        }
7488
7489 4
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
7490
    }
7491
7492 1
    private function parseRichText($is)
7493
    {
7494 1
        $value = new RichText();
7495 1
        $value->createText($is);
7496
7497 1
        return $value;
7498
    }
7499
}
7500