Completed
Push — develop ( 91417a...f02c33 )
by Adrien
18:11
created

Excel5::readBof()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
c 2
b 0
f 0
nc 4
nop 0
dl 0
loc 33
ccs 0
cts 24
cp 0
crap 56
rs 6.7272
1
<?php
2
3
namespace PhpSpreadsheet\Reader;
4
5
/**
6
 * Copyright (c) 2006 - 2016 PhpSpreadsheet
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 *
22
 * @category   PhpSpreadsheet
23
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
24
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
25
 * @version    ##VERSION##, ##DATE##
26
 */
27
28
// Original file header of ParseXL (used as the base for this class):
29
// --------------------------------------------------------------------------------
30
// Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
31
// trex005, and mmp11 (SourceForge.net)
32
// http://sourceforge.net/projects/phpexcelreader/
33
// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
34
//     Modelled moreso after Perl Excel Parse/Write modules
35
//     Added Parse_Excel_Spreadsheet object
36
//         Reads a whole worksheet or tab as row,column array or as
37
//         associated hash of indexed rows and named column fields
38
//     Added variables for worksheet (tab) indexes and names
39
//     Added an object call for loading individual woorksheets
40
//     Changed default indexing defaults to 0 based arrays
41
//     Fixed date/time and percent formats
42
//     Includes patches found at SourceForge...
43
//         unicode patch by nobody
44
//         unpack("d") machine depedency patch by matchy
45
//         boundsheet utf16 patch by bjaenichen
46
//     Renamed functions for shorter names
47
//     General code cleanup and rigor, including <80 column width
48
//     Included a testcase Excel file and PHP example calls
49
//     Code works for PHP 5.x
50
51
// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
52
// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
53
//     Decoding of formula conditions, results, and tokens.
54
//     Support for user-defined named cells added as an array "namedcells"
55
//         Patch code for user-defined named cells supports single cells only.
56
//         NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
57
//         external sheet reference structure
58
class Excel5 extends BaseReader implements IReader
59
{
60
    // ParseXL definitions
61
    const XLS_BIFF8 = 0x0600;
62
    const XLS_BIFF7 = 0x0500;
63
    const XLS_WORKBOOKGLOBALS = 0x0005;
64
    const XLS_WORKSHEET = 0x0010;
65
66
    // record identifiers
67
    const XLS_TYPE_FORMULA = 0x0006;
68
    const XLS_TYPE_EOF = 0x000a;
69
    const XLS_TYPE_PROTECT = 0x0012;
70
    const XLS_TYPE_OBJECTPROTECT = 0x0063;
71
    const XLS_TYPE_SCENPROTECT = 0x00dd;
72
    const XLS_TYPE_PASSWORD = 0x0013;
73
    const XLS_TYPE_HEADER = 0x0014;
74
    const XLS_TYPE_FOOTER = 0x0015;
75
    const XLS_TYPE_EXTERNSHEET = 0x0017;
76
    const XLS_TYPE_DEFINEDNAME = 0x0018;
77
    const XLS_TYPE_VERTICALPAGEBREAKS = 0x001a;
78
    const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001b;
79
    const XLS_TYPE_NOTE = 0x001c;
80
    const XLS_TYPE_SELECTION = 0x001d;
81
    const XLS_TYPE_DATEMODE = 0x0022;
82
    const XLS_TYPE_EXTERNNAME = 0x0023;
83
    const XLS_TYPE_LEFTMARGIN = 0x0026;
84
    const XLS_TYPE_RIGHTMARGIN = 0x0027;
85
    const XLS_TYPE_TOPMARGIN = 0x0028;
86
    const XLS_TYPE_BOTTOMMARGIN = 0x0029;
87
    const XLS_TYPE_PRINTGRIDLINES = 0x002b;
88
    const XLS_TYPE_FILEPASS = 0x002f;
89
    const XLS_TYPE_FONT = 0x0031;
90
    const XLS_TYPE_CONTINUE = 0x003c;
91
    const XLS_TYPE_PANE = 0x0041;
92
    const XLS_TYPE_CODEPAGE = 0x0042;
93
    const XLS_TYPE_DEFCOLWIDTH = 0x0055;
94
    const XLS_TYPE_OBJ = 0x005d;
95
    const XLS_TYPE_COLINFO = 0x007d;
96
    const XLS_TYPE_IMDATA = 0x007f;
97
    const XLS_TYPE_SHEETPR = 0x0081;
98
    const XLS_TYPE_HCENTER = 0x0083;
99
    const XLS_TYPE_VCENTER = 0x0084;
100
    const XLS_TYPE_SHEET = 0x0085;
101
    const XLS_TYPE_PALETTE = 0x0092;
102
    const XLS_TYPE_SCL = 0x00a0;
103
    const XLS_TYPE_PAGESETUP = 0x00a1;
104
    const XLS_TYPE_MULRK = 0x00bd;
105
    const XLS_TYPE_MULBLANK = 0x00be;
106
    const XLS_TYPE_DBCELL = 0x00d7;
107
    const XLS_TYPE_XF = 0x00e0;
108
    const XLS_TYPE_MERGEDCELLS = 0x00e5;
109
    const XLS_TYPE_MSODRAWINGGROUP = 0x00eb;
110
    const XLS_TYPE_MSODRAWING = 0x00ec;
111
    const XLS_TYPE_SST = 0x00fc;
112
    const XLS_TYPE_LABELSST = 0x00fd;
113
    const XLS_TYPE_EXTSST = 0x00ff;
114
    const XLS_TYPE_EXTERNALBOOK = 0x01ae;
115
    const XLS_TYPE_DATAVALIDATIONS = 0x01b2;
116
    const XLS_TYPE_TXO = 0x01b6;
117
    const XLS_TYPE_HYPERLINK = 0x01b8;
118
    const XLS_TYPE_DATAVALIDATION = 0x01be;
119
    const XLS_TYPE_DIMENSION = 0x0200;
120
    const XLS_TYPE_BLANK = 0x0201;
121
    const XLS_TYPE_NUMBER = 0x0203;
122
    const XLS_TYPE_LABEL = 0x0204;
123
    const XLS_TYPE_BOOLERR = 0x0205;
124
    const XLS_TYPE_STRING = 0x0207;
125
    const XLS_TYPE_ROW = 0x0208;
126
    const XLS_TYPE_INDEX = 0x020b;
127
    const XLS_TYPE_ARRAY = 0x0221;
128
    const XLS_TYPE_DEFAULTROWHEIGHT = 0x0225;
129
    const XLS_TYPE_WINDOW2 = 0x023e;
130
    const XLS_TYPE_RK = 0x027e;
131
    const XLS_TYPE_STYLE = 0x0293;
132
    const XLS_TYPE_FORMAT = 0x041e;
133
    const XLS_TYPE_SHAREDFMLA = 0x04bc;
134
    const XLS_TYPE_BOF = 0x0809;
135
    const XLS_TYPE_SHEETPROTECTION = 0x0867;
136
    const XLS_TYPE_RANGEPROTECTION = 0x0868;
137
    const XLS_TYPE_SHEETLAYOUT = 0x0862;
138
    const XLS_TYPE_XFEXT = 0x087d;
139
    const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
140
    const XLS_TYPE_UNKNOWN = 0xffff;
141
142
    // Encryption type
143
    const MS_BIFF_CRYPTO_NONE = 0;
144
    const MS_BIFF_CRYPTO_XOR = 1;
145
    const MS_BIFF_CRYPTO_RC4 = 2;
146
147
    // Size of stream blocks when using RC4 encryption
148
    const REKEY_BLOCK = 0x400;
149
150
    /**
151
     * Summary Information stream data.
152
     *
153
     * @var string
154
     */
155
    private $summaryInformation;
156
157
    /**
158
     * Extended Summary Information stream data.
159
     *
160
     * @var string
161
     */
162
    private $documentSummaryInformation;
163
164
    /**
165
     * User-Defined Properties stream data.
166
     *
167
     * @var string
168
     */
169
    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...
170
171
    /**
172
     * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
173
     *
174
     * @var string
175
     */
176
    private $data;
177
178
    /**
179
     * Size in bytes of $this->data
180
     *
181
     * @var int
182
     */
183
    private $dataSize;
184
185
    /**
186
     * Current position in stream
187
     *
188
     * @var int
189
     */
190
    private $pos;
191
192
    /**
193
     * Workbook to be returned by the reader.
194
     *
195
     * @var \PhpSpreadsheet\Spreadsheet
196
     */
197
    private $spreadsheet;
198
199
    /**
200
     * Worksheet that is currently being built by the reader.
201
     *
202
     * @var Worksheet
203
     */
204
    private $phpSheet;
205
206
    /**
207
     * BIFF version
208
     *
209
     * @var int
210
     */
211
    private $version;
212
213
    /**
214
     * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
215
     * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
216
     *
217
     * @var string
218
     */
219
    private $codepage;
220
221
    /**
222
     * Shared formats
223
     *
224
     * @var array
225
     */
226
    private $formats;
227
228
    /**
229
     * Shared fonts
230
     *
231
     * @var array
232
     */
233
    private $objFonts;
234
235
    /**
236
     * Color palette
237
     *
238
     * @var array
239
     */
240
    private $palette;
241
242
    /**
243
     * Worksheets
244
     *
245
     * @var array
246
     */
247
    private $sheets;
248
249
    /**
250
     * External books
251
     *
252
     * @var array
253
     */
254
    private $externalBooks;
255
256
    /**
257
     * REF structures. Only applies to BIFF8.
258
     *
259
     * @var array
260
     */
261
    private $ref;
262
263
    /**
264
     * External names
265
     *
266
     * @var array
267
     */
268
    private $externalNames;
269
270
    /**
271
     * Defined names
272
     *
273
     * @var array
274
     */
275
    private $definedname;
276
277
    /**
278
     * Shared strings. Only applies to BIFF8.
279
     *
280
     * @var array
281
     */
282
    private $sst;
283
284
    /**
285
     * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
286
     *
287
     * @var bool
288
     */
289
    private $frozen;
290
291
    /**
292
     * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
293
     *
294
     * @var bool
295
     */
296
    private $isFitToPages;
297
298
    /**
299
     * Objects. One OBJ record contributes with one entry.
300
     *
301
     * @var array
302
     */
303
    private $objs;
304
305
    /**
306
     * Text Objects. One TXO record corresponds with one entry.
307
     *
308
     * @var array
309
     */
310
    private $textObjects;
311
312
    /**
313
     * Cell Annotations (BIFF8)
314
     *
315
     * @var array
316
     */
317
    private $cellNotes;
318
319
    /**
320
     * The combined MSODRAWINGGROUP data
321
     *
322
     * @var string
323
     */
324
    private $drawingGroupData;
325
326
    /**
327
     * The combined MSODRAWING data (per sheet)
328
     *
329
     * @var string
330
     */
331
    private $drawingData;
332
333
    /**
334
     * Keep track of XF index
335
     *
336
     * @var int
337
     */
338
    private $xfIndex;
339
340
    /**
341
     * Mapping of XF index (that is a cell XF) to final index in cellXf collection
342
     *
343
     * @var array
344
     */
345
    private $mapCellXfIndex;
346
347
    /**
348
     * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
349
     *
350
     * @var array
351
     */
352
    private $mapCellStyleXfIndex;
353
354
    /**
355
     * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
356
     *
357
     * @var array
358
     */
359
    private $sharedFormulas;
360
361
    /**
362
     * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
363
     * refers to a shared formula.
364
     *
365
     * @var array
366
     */
367
    private $sharedFormulaParts;
368
369
    /**
370
     * The type of encryption in use
371
     *
372
     * @var int
373
     */
374
    private $encryption = 0;
375
376
    /**
377
     * The position in the stream after which contents are encrypted
378
     *
379
     * @var int
380
     */
381
    private $encryptionStartPos = false;
382
383
    /**
384
     * The current RC4 decryption object
385
     *
386
     * @var Excel5\RC4
387
     */
388
    private $rc4Key = null;
389
390
    /**
391
     * The position in the stream that the RC4 decryption object was left at
392
     *
393
     * @var int
394
     */
395
    private $rc4Pos = 0;
396
397
    /**
398
     * The current MD5 context state
399
     *
400
     * @var string
401
     */
402
    private $md5Ctxt = null;
403
404
    /**
405
     * Create a new Excel5 Reader instance
406
     */
407
    public function __construct()
408
    {
409
        $this->readFilter = new DefaultReadFilter();
410
    }
411
412
    /**
413
     * Can the current IReader read the file?
414
     *
415
     * @param     string         $pFilename
416
     * @throws Exception
417
     * @return     bool
418
     */
419
    public function canRead($pFilename)
420
    {
421
        // Check if file exists
422
        if (!file_exists($pFilename)) {
423
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
424
        }
425
426
        try {
427
            // Use ParseXL for the hard work.
428
            $ole = new \PhpSpreadsheet\Shared\OLERead();
429
430
            // get excel data
431
            $res = $ole->read($pFilename);
0 ignored issues
show
Unused Code introduced by
$res 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...
Bug introduced by
Are you sure the assignment to $res is correct as $ole->read($pFilename) (which targets PhpSpreadsheet\Shared\OLERead::read()) 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...
432
433
            return true;
434
        } catch (\PhpSpreadsheet\Exception $e) {
435
            return false;
436
        }
437
    }
438
439
    /**
440
     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object
441
     *
442
     * @param     string         $pFilename
443
     * @throws     Exception
444
     */
445
    public function listWorksheetNames($pFilename)
446
    {
447
        // Check if file exists
448
        if (!file_exists($pFilename)) {
449
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
450
        }
451
452
        $worksheetNames = [];
453
454
        // Read the OLE file
455
        $this->loadOLE($pFilename);
456
457
        // total byte size of Excel data (workbook global substream + sheet substreams)
458
        $this->dataSize = strlen($this->data);
459
460
        $this->pos = 0;
461
        $this->sheets = [];
462
463
        // Parse Workbook Global Substream
464 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...
465
            $code = self::getInt2d($this->data, $this->pos);
466
467
            switch ($code) {
468
                case self::XLS_TYPE_BOF:
469
                    $this->readBof();
470
                    break;
471
                case self::XLS_TYPE_SHEET:
472
                    $this->readSheet();
473
                    break;
474
                case self::XLS_TYPE_EOF:
475
                    $this->readDefault();
476
                    break 2;
477
                default:
478
                    $this->readDefault();
479
                    break;
480
            }
481
        }
482
483
        foreach ($this->sheets as $sheet) {
484
            if ($sheet['sheetType'] != 0x00) {
485
                // 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...
486
                continue;
487
            }
488
489
            $worksheetNames[] = $sheet['name'];
490
        }
491
492
        return $worksheetNames;
493
    }
494
495
    /**
496
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
497
     *
498
     * @param   string     $pFilename
499
     * @throws   Exception
500
     */
501
    public function listWorksheetInfo($pFilename)
502
    {
503
        // Check if file exists
504
        if (!file_exists($pFilename)) {
505
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
506
        }
507
508
        $worksheetInfo = [];
509
510
        // Read the OLE file
511
        $this->loadOLE($pFilename);
512
513
        // total byte size of Excel data (workbook global substream + sheet substreams)
514
        $this->dataSize = strlen($this->data);
515
516
        // initialize
517
        $this->pos = 0;
518
        $this->sheets = [];
519
520
        // Parse Workbook Global Substream
521 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...
522
            $code = self::getInt2d($this->data, $this->pos);
523
524
            switch ($code) {
525
                case self::XLS_TYPE_BOF:
526
                    $this->readBof();
527
                    break;
528
                case self::XLS_TYPE_SHEET:
529
                    $this->readSheet();
530
                    break;
531
                case self::XLS_TYPE_EOF:
532
                    $this->readDefault();
533
                    break 2;
534
                default:
535
                    $this->readDefault();
536
                    break;
537
            }
538
        }
539
540
        // Parse the individual sheets
541
        foreach ($this->sheets as $sheet) {
542
            if ($sheet['sheetType'] != 0x00) {
543
                // 0x00: Worksheet
544
                // 0x02: Chart
545
                // 0x06: Visual Basic module
546
                continue;
547
            }
548
549
            $tmpInfo = [];
550
            $tmpInfo['worksheetName'] = $sheet['name'];
551
            $tmpInfo['lastColumnLetter'] = 'A';
552
            $tmpInfo['lastColumnIndex'] = 0;
553
            $tmpInfo['totalRows'] = 0;
554
            $tmpInfo['totalColumns'] = 0;
555
556
            $this->pos = $sheet['offset'];
557
558
            while ($this->pos <= $this->dataSize - 4) {
559
                $code = self::getInt2d($this->data, $this->pos);
560
561
                switch ($code) {
562
                    case self::XLS_TYPE_RK:
563
                    case self::XLS_TYPE_LABELSST:
564
                    case self::XLS_TYPE_NUMBER:
565
                    case self::XLS_TYPE_FORMULA:
566
                    case self::XLS_TYPE_BOOLERR:
567
                    case self::XLS_TYPE_LABEL:
568
                        $length = self::getInt2d($this->data, $this->pos + 2);
569
                        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
570
571
                        // move stream pointer to next record
572
                        $this->pos += 4 + $length;
573
574
                        $rowIndex = self::getInt2d($recordData, 0) + 1;
575
                        $columnIndex = self::getInt2d($recordData, 2);
576
577
                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
578
                        $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
579
                        break;
580
                    case self::XLS_TYPE_BOF:
581
                        $this->readBof();
582
                        break;
583
                    case self::XLS_TYPE_EOF:
584
                        $this->readDefault();
585
                        break 2;
586
                    default:
587
                        $this->readDefault();
588
                        break;
589
                }
590
            }
591
592
            $tmpInfo['lastColumnLetter'] = \PhpSpreadsheet\Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
593
            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
594
595
            $worksheetInfo[] = $tmpInfo;
596
        }
597
598
        return $worksheetInfo;
599
    }
600
601
    /**
602
     * Loads PhpSpreadsheet from file
603
     *
604
     * @param     string         $pFilename
605
     * @throws    Exception
606
     * @return    \PhpSpreadsheet\Spreadsheet
607
     */
608
    public function load($pFilename)
609
    {
610
        // Read the OLE file
611
        $this->loadOLE($pFilename);
612
613
        // Initialisations
614
        $this->spreadsheet = new \PhpSpreadsheet\Spreadsheet();
615
        $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
616
        if (!$this->readDataOnly) {
617
            $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
618
            $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
619
        }
620
621
        // Read the summary information stream (containing meta data)
622
        $this->readSummaryInformation();
623
624
        // Read the Additional document summary information stream (containing application-specific meta data)
625
        $this->readDocumentSummaryInformation();
626
627
        // total byte size of Excel data (workbook global substream + sheet substreams)
628
        $this->dataSize = strlen($this->data);
629
630
        // initialize
631
        $this->pos = 0;
632
        $this->codepage = 'CP1252';
633
        $this->formats = [];
634
        $this->objFonts = [];
635
        $this->palette = [];
636
        $this->sheets = [];
637
        $this->externalBooks = [];
638
        $this->ref = [];
639
        $this->definedname = [];
640
        $this->sst = [];
641
        $this->drawingGroupData = '';
642
        $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...
643
        $this->mapCellXfIndex = [];
644
        $this->mapCellStyleXfIndex = [];
645
646
        // Parse Workbook Global Substream
647
        while ($this->pos < $this->dataSize) {
648
            $code = self::getInt2d($this->data, $this->pos);
649
650
            switch ($code) {
651
                case self::XLS_TYPE_BOF:
652
                    $this->readBof();
653
                    break;
654
                case self::XLS_TYPE_FILEPASS:
655
                    $this->readFilepass();
656
                    break;
657
                case self::XLS_TYPE_CODEPAGE:
658
                    $this->readCodepage();
659
                    break;
660
                case self::XLS_TYPE_DATEMODE:
661
                    $this->readDateMode();
662
                    break;
663
                case self::XLS_TYPE_FONT:
664
                    $this->readFont();
665
                    break;
666
                case self::XLS_TYPE_FORMAT:
667
                    $this->readFormat();
668
                    break;
669
                case self::XLS_TYPE_XF:
670
                    $this->readXf();
671
                    break;
672
                case self::XLS_TYPE_XFEXT:
673
                    $this->readXfExt();
674
                    break;
675
                case self::XLS_TYPE_STYLE:
676
                    $this->readStyle();
677
                    break;
678
                case self::XLS_TYPE_PALETTE:
679
                    $this->readPalette();
680
                    break;
681
                case self::XLS_TYPE_SHEET:
682
                    $this->readSheet();
683
                    break;
684
                case self::XLS_TYPE_EXTERNALBOOK:
685
                    $this->readExternalBook();
686
                    break;
687
                case self::XLS_TYPE_EXTERNNAME:
688
                    $this->readExternName();
689
                    break;
690
                case self::XLS_TYPE_EXTERNSHEET:
691
                    $this->readExternSheet();
692
                    break;
693
                case self::XLS_TYPE_DEFINEDNAME:
694
                    $this->readDefinedName();
695
                    break;
696
                case self::XLS_TYPE_MSODRAWINGGROUP:
697
                    $this->readMsoDrawingGroup();
698
                    break;
699
                case self::XLS_TYPE_SST:
700
                    $this->readSst();
701
                    break;
702
                case self::XLS_TYPE_EOF:
703
                    $this->readDefault();
704
                    break 2;
705
                default:
706
                    $this->readDefault();
707
                    break;
708
            }
709
        }
710
711
        // Resolve indexed colors for font, fill, and border colors
712
        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
713
        if (!$this->readDataOnly) {
714
            foreach ($this->objFonts as $objFont) {
715 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...
716
                    $color = Excel5\Color::map($objFont->colorIndex, $this->palette, $this->version);
717
                    $objFont->getColor()->setRGB($color['rgb']);
718
                }
719
            }
720
721
            foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
722
                // fill start and end color
723
                $fill = $objStyle->getFill();
724
725
                if (isset($fill->startcolorIndex)) {
726
                    $startColor = Excel5\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...
727
                    $fill->getStartColor()->setRGB($startColor['rgb']);
728
                }
729
                if (isset($fill->endcolorIndex)) {
730
                    $endColor = Excel5\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...
731
                    $fill->getEndColor()->setRGB($endColor['rgb']);
732
                }
733
734
                // border colors
735
                $top = $objStyle->getBorders()->getTop();
736
                $right = $objStyle->getBorders()->getRight();
737
                $bottom = $objStyle->getBorders()->getBottom();
738
                $left = $objStyle->getBorders()->getLeft();
739
                $diagonal = $objStyle->getBorders()->getDiagonal();
740
741 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...
742
                    $borderTopColor = Excel5\Color::map($top->colorIndex, $this->palette, $this->version);
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in 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...
743
                    $top->getColor()->setRGB($borderTopColor['rgb']);
744
                }
745 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...
746
                    $borderRightColor = Excel5\Color::map($right->colorIndex, $this->palette, $this->version);
747
                    $right->getColor()->setRGB($borderRightColor['rgb']);
748
                }
749 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...
750
                    $borderBottomColor = Excel5\Color::map($bottom->colorIndex, $this->palette, $this->version);
751
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
752
                }
753 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...
754
                    $borderLeftColor = Excel5\Color::map($left->colorIndex, $this->palette, $this->version);
755
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
756
                }
757 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...
758
                    $borderDiagonalColor = Excel5\Color::map($diagonal->colorIndex, $this->palette, $this->version);
759
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
760
                }
761
            }
762
        }
763
764
        // treat MSODRAWINGGROUP records, workbook-level Escher
765
        if (!$this->readDataOnly && $this->drawingGroupData) {
766
            $escherWorkbook = new \PhpSpreadsheet\Shared\Escher();
767
            $reader = new Excel5\Escher($escherWorkbook);
768
            $escherWorkbook = $reader->load($this->drawingGroupData);
769
        }
770
771
        // Parse the individual sheets
772
        foreach ($this->sheets as $sheet) {
773
            if ($sheet['sheetType'] != 0x00) {
774
                // 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...
775
                continue;
776
            }
777
778
            // check if sheet should be skipped
779
            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
780
                continue;
781
            }
782
783
            // add sheet to PhpSpreadsheet object
784
            $this->phpSheet = $this->spreadsheet->createSheet();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->spreadsheet->createSheet() of type object<PhpSpreadsheet\Worksheet> is incompatible with the declared type object<PhpSpreadsheet\Reader\Worksheet> of property $phpSheet.

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

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

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

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
1185
                    //    Named Value
1186
                    //    TODO Provide support for named values
1187
                }
1188
            }
1189
        }
1190
        $this->data = null;
1191
1192
        return $this->spreadsheet;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->spreadsheet; (PhpSpreadsheet\Spreadsheet) is incompatible with the return type declared by the interface PhpSpreadsheet\Reader\IReader::load of type PhpSpreadsheet\Reader\PhpSpreadsheet.

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...
1193
    }
1194
1195
    /**
1196
     * Read record data from stream, decrypting as required
1197
     *
1198
     * @param string $data   Data stream to read from
1199
     * @param int    $pos    Position to start reading from
1200
     * @param int    $len    Record data length
1201
     *
1202
     * @return string Record data
1203
     */
1204
    private function readRecordData($data, $pos, $len)
1205
    {
1206
        $data = substr($data, $pos, $len);
1207
1208
        // File not encrypted, or record before encryption start point
1209
        if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
1210
            return $data;
1211
        }
1212
1213
        $recordData = '';
1214
        if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
1215
            $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
1216
            $block = floor($pos / self::REKEY_BLOCK);
1217
            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
1218
1219
            // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
1220
            // at a point earlier in the current block, re-use it as we can save some time.
1221
            if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
1222
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1223
                $step = $pos % self::REKEY_BLOCK;
1224
            } else {
1225
                $step = $pos - $this->rc4Pos;
1226
            }
1227
            $this->rc4Key->RC4(str_repeat("\0", $step));
1228
1229
            // Decrypt record data (re-keying at the end of every block)
1230
            while ($block != $endBlock) {
1231
                $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
1232
                $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
1233
                $data = substr($data, $step);
1234
                $pos += $step;
1235
                $len -= $step;
1236
                ++$block;
1237
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1238
            }
1239
            $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
1240
1241
            // Keep track of the position of this decryptor.
1242
            // We'll try and re-use it later if we can to speed things up
1243
            $this->rc4Pos = $pos + $len;
1244
        } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
1245
            throw new Exception('XOr encryption not supported');
1246
        }
1247
1248
        return $recordData;
1249
    }
1250
1251
    /**
1252
     * Use OLE reader to extract the relevant data streams from the OLE file
1253
     *
1254
     * @param string $pFilename
1255
     */
1256
    private function loadOLE($pFilename)
1257
    {
1258
        // OLE reader
1259
        $ole = new \PhpSpreadsheet\Shared\OLERead();
1260
        // get excel data,
1261
        $res = $ole->read($pFilename);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $res is correct as $ole->read($pFilename) (which targets PhpSpreadsheet\Shared\OLERead::read()) 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...
Unused Code introduced by
$res 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...
1262
        // Get workbook data: workbook stream + sheet streams
1263
        $this->data = $ole->getStream($ole->wrkbook);
1264
        // Get summary information data
1265
        $this->summaryInformation = $ole->getStream($ole->summaryInformation);
1266
        // Get additional document summary information data
1267
        $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1268
    }
1269
1270
    /**
1271
     * Read summary information
1272
     */
1273
    private function readSummaryInformation()
1274
    {
1275
        if (!isset($this->summaryInformation)) {
1276
            return;
1277
        }
1278
1279
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1280
        // 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...
1281
        // offset: 4; size: 2; OS version
1282
        // offset: 6; size: 2; OS indicator
1283
        // offset: 8; size: 16
1284
        // offset: 24; size: 4; section count
1285
        $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...
1286
1287
        // 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
1288
        // offset: 44; size: 4
1289
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1290
1291
        // section header
1292
        // offset: $secOffset; size: 4; section length
1293
        $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...
1294
1295
        // offset: $secOffset+4; size: 4; property count
1296
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1297
1298
        // initialize code page (used to resolve string values)
1299
        $codePage = 'CP1252';
1300
1301
        // 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...
1302
        // loop through property decarations and properties
1303
        for ($i = 0; $i < $countProperties; ++$i) {
1304
            // 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...
1305
            $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
1306
1307
            // Use value of property id as appropriate
1308
            // 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...
1309
            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
1310
1311
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1312
1313
            // initialize property value
1314
            $value = null;
1315
1316
            // extract property value based on property type
1317
            switch ($type) {
1318
                case 0x02: // 2 byte signed integer
1319
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1320
                    break;
1321
                case 0x03: // 4 byte signed integer
1322
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1323
                    break;
1324
                case 0x13: // 4 byte unsigned integer
1325
                    // not needed yet, fix later if necessary
1326
                    break;
1327 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...
1328
                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1329
                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1330
                    $value = \PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1331
                    $value = rtrim($value);
1332
                    break;
1333 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...
1334
                    // PHP-time
1335
                    $value = \PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1336
                    break;
1337
                case 0x47: // Clipboard format
1338
                    // not needed yet, fix later if necessary
1339
                    break;
1340
            }
1341
1342
            switch ($id) {
1343
                case 0x01:    //    Code Page
1344
                    $codePage = \PhpSpreadsheet\Shared\CodePage::numberToName($value);
1345
                    break;
1346
                case 0x02:    //    Title
1347
                    $this->spreadsheet->getProperties()->setTitle($value);
1348
                    break;
1349
                case 0x03:    //    Subject
1350
                    $this->spreadsheet->getProperties()->setSubject($value);
1351
                    break;
1352
                case 0x04:    //    Author (Creator)
1353
                    $this->spreadsheet->getProperties()->setCreator($value);
1354
                    break;
1355
                case 0x05:    //    Keywords
1356
                    $this->spreadsheet->getProperties()->setKeywords($value);
1357
                    break;
1358
                case 0x06:    //    Comments (Description)
1359
                    $this->spreadsheet->getProperties()->setDescription($value);
1360
                    break;
1361
                case 0x07:    //    Template
1362
                    //    Not supported by PhpSpreadsheet
1363
                    break;
1364
                case 0x08:    //    Last Saved By (LastModifiedBy)
1365
                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
1366
                    break;
1367
                case 0x09:    //    Revision
1368
                    //    Not supported by PhpSpreadsheet
1369
                    break;
1370
                case 0x0A:    //    Total Editing Time
1371
                    //    Not supported by PhpSpreadsheet
1372
                    break;
1373
                case 0x0B:    //    Last Printed
1374
                    //    Not supported by PhpSpreadsheet
1375
                    break;
1376
                case 0x0C:    //    Created Date/Time
1377
                    $this->spreadsheet->getProperties()->setCreated($value);
1378
                    break;
1379
                case 0x0D:    //    Modified Date/Time
1380
                    $this->spreadsheet->getProperties()->setModified($value);
1381
                    break;
1382
                case 0x0E:    //    Number of Pages
1383
                    //    Not supported by PhpSpreadsheet
1384
                    break;
1385
                case 0x0F:    //    Number of Words
1386
                    //    Not supported by PhpSpreadsheet
1387
                    break;
1388
                case 0x10:    //    Number of Characters
1389
                    //    Not supported by PhpSpreadsheet
1390
                    break;
1391
                case 0x11:    //    Thumbnail
1392
                    //    Not supported by PhpSpreadsheet
1393
                    break;
1394
                case 0x12:    //    Name of creating application
1395
                    //    Not supported by PhpSpreadsheet
1396
                    break;
1397
                case 0x13:    //    Security
1398
                    //    Not supported by PhpSpreadsheet
1399
                    break;
1400
            }
1401
        }
1402
    }
1403
1404
    /**
1405
     * Read additional document summary information
1406
     */
1407
    private function readDocumentSummaryInformation()
1408
    {
1409
        if (!isset($this->documentSummaryInformation)) {
1410
            return;
1411
        }
1412
1413
        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
1414
        //    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...
1415
        //    offset: 4;    size: 2;    OS version
1416
        //    offset: 6;    size: 2;    OS indicator
1417
        //    offset: 8;    size: 16
1418
        //    offset: 24;    size: 4;    section count
1419
        $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...
1420
1421
        // 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
1422
        // offset: 44;    size: 4;    first section offset
1423
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1424
1425
        //    section header
1426
        //    offset: $secOffset;    size: 4;    section length
1427
        $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...
1428
1429
        //    offset: $secOffset+4;    size: 4;    property count
1430
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1431
1432
        // initialize code page (used to resolve string values)
1433
        $codePage = 'CP1252';
1434
1435
        //    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...
1436
        //    loop through property decarations and properties
1437
        for ($i = 0; $i < $countProperties; ++$i) {
1438
            //    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...
1439
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
1440
1441
            // Use value of property id as appropriate
1442
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1443
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
1444
1445
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1446
1447
            // initialize property value
1448
            $value = null;
1449
1450
            // extract property value based on property type
1451
            switch ($type) {
1452
                case 0x02:    //    2 byte signed integer
1453
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1454
                    break;
1455
                case 0x03:    //    4 byte signed integer
1456
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1457
                    break;
1458
                case 0x0B:  // Boolean
1459
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1460
                    $value = ($value == 0 ? false : true);
1461
                    break;
1462
                case 0x13:    //    4 byte unsigned integer
1463
                    // not needed yet, fix later if necessary
1464
                    break;
1465 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...
1466
                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1467
                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1468
                    $value = \PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1469
                    $value = rtrim($value);
1470
                    break;
1471 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...
1472
                    // PHP-Time
1473
                    $value = \PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1474
                    break;
1475
                case 0x47:    //    Clipboard format
1476
                    // not needed yet, fix later if necessary
1477
                    break;
1478
            }
1479
1480
            switch ($id) {
1481
                case 0x01:    //    Code Page
1482
                    $codePage = \PhpSpreadsheet\Shared\CodePage::numberToName($value);
1483
                    break;
1484
                case 0x02:    //    Category
1485
                    $this->spreadsheet->getProperties()->setCategory($value);
1486
                    break;
1487
                case 0x03:    //    Presentation Target
1488
                    //    Not supported by PhpSpreadsheet
1489
                    break;
1490
                case 0x04:    //    Bytes
1491
                    //    Not supported by PhpSpreadsheet
1492
                    break;
1493
                case 0x05:    //    Lines
1494
                    //    Not supported by PhpSpreadsheet
1495
                    break;
1496
                case 0x06:    //    Paragraphs
1497
                    //    Not supported by PhpSpreadsheet
1498
                    break;
1499
                case 0x07:    //    Slides
1500
                    //    Not supported by PhpSpreadsheet
1501
                    break;
1502
                case 0x08:    //    Notes
1503
                    //    Not supported by PhpSpreadsheet
1504
                    break;
1505
                case 0x09:    //    Hidden Slides
1506
                    //    Not supported by PhpSpreadsheet
1507
                    break;
1508
                case 0x0A:    //    MM Clips
1509
                    //    Not supported by PhpSpreadsheet
1510
                    break;
1511
                case 0x0B:    //    Scale Crop
1512
                    //    Not supported by PhpSpreadsheet
1513
                    break;
1514
                case 0x0C:    //    Heading Pairs
1515
                    //    Not supported by PhpSpreadsheet
1516
                    break;
1517
                case 0x0D:    //    Titles of Parts
1518
                    //    Not supported by PhpSpreadsheet
1519
                    break;
1520
                case 0x0E:    //    Manager
1521
                    $this->spreadsheet->getProperties()->setManager($value);
1522
                    break;
1523
                case 0x0F:    //    Company
1524
                    $this->spreadsheet->getProperties()->setCompany($value);
1525
                    break;
1526
                case 0x10:    //    Links up-to-date
1527
                    //    Not supported by PhpSpreadsheet
1528
                    break;
1529
            }
1530
        }
1531
    }
1532
1533
    /**
1534
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1535
     */
1536
    private function readDefault()
1537
    {
1538
        $length = self::getInt2d($this->data, $this->pos + 2);
1539
1540
        // move stream pointer to next record
1541
        $this->pos += 4 + $length;
1542
    }
1543
1544
    /**
1545
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1546
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1547
     */
1548
    private function readNote()
1549
    {
1550
        $length = self::getInt2d($this->data, $this->pos + 2);
1551
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1552
1553
        // move stream pointer to next record
1554
        $this->pos += 4 + $length;
1555
1556
        if ($this->readDataOnly) {
1557
            return;
1558
        }
1559
1560
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1561
        if ($this->version == self::XLS_BIFF8) {
1562
            $noteObjID = self::getInt2d($recordData, 6);
1563
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1564
            $noteAuthor = $noteAuthor['value'];
1565
            $this->cellNotes[$noteObjID] = [
1566
                'cellRef' => $cellAddress,
1567
                'objectID' => $noteObjID,
1568
                'author' => $noteAuthor,
1569
            ];
1570
        } else {
1571
            $extension = false;
1572
            if ($cellAddress == '$B$65536') {
1573
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1574
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1575
                //        max 2048 bytes will probably throw a wobbly.
1576
                $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...
1577
                $extension = true;
1578
                $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...
1579
            }
1580
1581
            $cellAddress = str_replace('$', '', $cellAddress);
1582
            $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...
1583
            $noteText = trim(substr($recordData, 6));
1584
1585
            if ($extension) {
1586
                //    Concatenate this extension with the currently set comment for the cell
1587
                $comment = $this->phpSheet->getComment($cellAddress);
1588
                $commentText = $comment->getText()->getPlainText();
1589
                $comment->setText($this->parseRichText($commentText . $noteText));
1590
            } else {
1591
                //    Set comment for the cell
1592
                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
1593
//                                                    ->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...
1594
            }
1595
        }
1596
    }
1597
1598
    /**
1599
     *    The TEXT Object record contains the text associated with a cell annotation.
1600
     */
1601
    private function readTextObject()
1602
    {
1603
        $length = self::getInt2d($this->data, $this->pos + 2);
1604
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1605
1606
        // move stream pointer to next record
1607
        $this->pos += 4 + $length;
1608
1609
        if ($this->readDataOnly) {
1610
            return;
1611
        }
1612
1613
        // recordData consists of an array of subrecords looking like this:
1614
        //    grbit: 2 bytes; Option Flags
1615
        //    rot: 2 bytes; rotation
1616
        //    cchText: 2 bytes; length of the text (in the first continue record)
1617
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1618
        // followed by the continuation records containing the actual text and formatting
1619
        $grbitOpts = self::getInt2d($recordData, 0);
1620
        $rot = self::getInt2d($recordData, 2);
1621
        $cchText = self::getInt2d($recordData, 10);
1622
        $cbRuns = self::getInt2d($recordData, 12);
1623
        $text = $this->getSplicedRecordData();
1624
1625
        $this->textObjects[$this->textObjRef] = [
1626
            'text' => substr($text['recordData'], $text['spliceOffsets'][0] + 1, $cchText),
1627
            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
1628
            'alignment' => $grbitOpts,
1629
            'rotation' => $rot,
1630
        ];
1631
    }
1632
1633
    /**
1634
     * Read BOF
1635
     */
1636
    private function readBof()
1637
    {
1638
        $length = self::getInt2d($this->data, $this->pos + 2);
1639
        $recordData = substr($this->data, $this->pos + 4, $length);
1640
1641
        // move stream pointer to next record
1642
        $this->pos += 4 + $length;
1643
1644
        // offset: 2; size: 2; type of the following data
1645
        $substreamType = self::getInt2d($recordData, 2);
1646
1647
        switch ($substreamType) {
1648
            case self::XLS_WORKBOOKGLOBALS:
1649
                $version = self::getInt2d($recordData, 0);
1650
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1651
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1652
                }
1653
                $this->version = $version;
1654
                break;
1655
            case self::XLS_WORKSHEET:
1656
                // do not use this version information for anything
1657
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1658
                break;
1659
            default:
1660
                // substream, e.g. chart
1661
                // just skip the entire substream
1662
                do {
1663
                    $code = self::getInt2d($this->data, $this->pos);
1664
                    $this->readDefault();
1665
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1666
                break;
1667
        }
1668
    }
1669
1670
    /**
1671
     * FILEPASS
1672
     *
1673
     * This record is part of the File Protection Block. It
1674
     * contains information about the read/write password of the
1675
     * file. All record contents following this record will be
1676
     * encrypted.
1677
     *
1678
     * --    "OpenOffice.org's Documentation of the Microsoft
1679
     *         Excel File Format"
1680
     *
1681
     * The decryption functions and objects used from here on in
1682
     * are based on the source of Spreadsheet-ParseExcel:
1683
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1684
     */
1685
    private function readFilepass()
1686
    {
1687
        $length = self::getInt2d($this->data, $this->pos + 2);
1688
1689
        if ($length != 54) {
1690
            throw new Exception('Unexpected file pass record length');
1691
        }
1692
1693
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1694
1695
        // move stream pointer to next record
1696
        $this->pos += 4 + $length;
1697
1698
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1699
            throw new Exception('Decryption password incorrect');
1700
        }
1701
1702
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1703
1704
        // Decryption required from the record after next onwards
1705
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1706
    }
1707
1708
    /**
1709
     * Make an RC4 decryptor for the given block
1710
     *
1711
     * @param int         Block for which to create decrypto
1712
     * @param string $valContext MD5 context state
1713
     *
1714
     * @return Excel5\RC4
1715
     */
1716
    private function makeKey($block, $valContext)
1717
    {
1718
        $pwarray = str_repeat("\0", 64);
1719
1720
        for ($i = 0; $i < 5; ++$i) {
1721
            $pwarray[$i] = $valContext[$i];
1722
        }
1723
1724
        $pwarray[5] = chr($block & 0xff);
1725
        $pwarray[6] = chr(($block >> 8) & 0xff);
1726
        $pwarray[7] = chr(($block >> 16) & 0xff);
1727
        $pwarray[8] = chr(($block >> 24) & 0xff);
1728
1729
        $pwarray[9] = "\x80";
1730
        $pwarray[56] = "\x48";
1731
1732
        $md5 = new Excel5\MD5();
1733
        $md5->add($pwarray);
1734
1735
        $s = $md5->getContext();
1736
1737
        return new Excel5\RC4($s);
1738
    }
1739
1740
    /**
1741
     * Verify RC4 file password
1742
     *
1743
     * @param string $password        Password to check
1744
     * @param string $docid           Document id
1745
     * @param string $salt_data       Salt data
1746
     * @param string $hashedsalt_data Hashed salt data
1747
     * @param string $valContext     Set to the MD5 context of the value
1748
     *
1749
     * @return bool Success
1750
     */
1751
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1752
    {
1753
        $pwarray = str_repeat("\0", 64);
1754
1755
        for ($i = 0; $i < strlen($password); ++$i) {
1756
            $o = ord(substr($password, $i, 1));
1757
            $pwarray[2 * $i] = chr($o & 0xff);
1758
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1759
        }
1760
        $pwarray[2 * $i] = chr(0x80);
1761
        $pwarray[56] = chr(($i << 4) & 0xff);
1762
1763
        $md5 = new Excel5\MD5();
1764
        $md5->add($pwarray);
1765
1766
        $mdContext1 = $md5->getContext();
1767
1768
        $offset = 0;
1769
        $keyoffset = 0;
1770
        $tocopy = 5;
1771
1772
        $md5->reset();
1773
1774
        while ($offset != 16) {
1775
            if ((64 - $offset) < 5) {
1776
                $tocopy = 64 - $offset;
1777
            }
1778
            for ($i = 0; $i <= $tocopy; ++$i) {
1779
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1780
            }
1781
            $offset += $tocopy;
1782
1783
            if ($offset == 64) {
1784
                $md5->add($pwarray);
1785
                $keyoffset = $tocopy;
1786
                $tocopy = 5 - $tocopy;
1787
                $offset = 0;
1788
                continue;
1789
            }
1790
1791
            $keyoffset = 0;
1792
            $tocopy = 5;
1793
            for ($i = 0; $i < 16; ++$i) {
1794
                $pwarray[$offset + $i] = $docid[$i];
1795
            }
1796
            $offset += 16;
1797
        }
1798
1799
        $pwarray[16] = "\x80";
1800
        for ($i = 0; $i < 47; ++$i) {
1801
            $pwarray[17 + $i] = "\0";
1802
        }
1803
        $pwarray[56] = "\x80";
1804
        $pwarray[57] = "\x0a";
1805
1806
        $md5->add($pwarray);
1807
        $valContext = $md5->getContext();
1808
1809
        $key = $this->makeKey(0, $valContext);
1810
1811
        $salt = $key->RC4($salt_data);
1812
        $hashedsalt = $key->RC4($hashedsalt_data);
1813
1814
        $salt .= "\x80" . str_repeat("\0", 47);
1815
        $salt[56] = "\x80";
1816
1817
        $md5->reset();
1818
        $md5->add($salt);
1819
        $mdContext2 = $md5->getContext();
1820
1821
        return $mdContext2 == $hashedsalt;
1822
    }
1823
1824
    /**
1825
     * CODEPAGE
1826
     *
1827
     * This record stores the text encoding used to write byte
1828
     * strings, stored as MS Windows code page identifier.
1829
     *
1830
     * --    "OpenOffice.org's Documentation of the Microsoft
1831
     *         Excel File Format"
1832
     */
1833 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...
1834
    {
1835
        $length = self::getInt2d($this->data, $this->pos + 2);
1836
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1837
1838
        // move stream pointer to next record
1839
        $this->pos += 4 + $length;
1840
1841
        // offset: 0; size: 2; code page identifier
1842
        $codepage = self::getInt2d($recordData, 0);
1843
1844
        $this->codepage = \PhpSpreadsheet\Shared\CodePage::numberToName($codepage);
1845
    }
1846
1847
    /**
1848
     * DATEMODE
1849
     *
1850
     * This record specifies the base date for displaying date
1851
     * values. All dates are stored as count of days past this
1852
     * base date. In BIFF2-BIFF4 this record is part of the
1853
     * Calculation Settings Block. In BIFF5-BIFF8 it is
1854
     * stored in the Workbook Globals Substream.
1855
     *
1856
     * --    "OpenOffice.org's Documentation of the Microsoft
1857
     *         Excel File Format"
1858
     */
1859
    private function readDateMode()
1860
    {
1861
        $length = self::getInt2d($this->data, $this->pos + 2);
1862
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1863
1864
        // move stream pointer to next record
1865
        $this->pos += 4 + $length;
1866
1867
        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
1868
        \PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900);
1869
        if (ord($recordData{0}) == 1) {
1870
            \PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpSpreadsheet\Shared\Date::CALENDAR_MAC_1904);
1871
        }
1872
    }
1873
1874
    /**
1875
     * Read a FONT record
1876
     */
1877
    private function readFont()
1878
    {
1879
        $length = self::getInt2d($this->data, $this->pos + 2);
1880
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1881
1882
        // move stream pointer to next record
1883
        $this->pos += 4 + $length;
1884
1885
        if (!$this->readDataOnly) {
1886
            $objFont = new \PhpSpreadsheet\Style\Font();
1887
1888
            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
1889
            $size = self::getInt2d($recordData, 0);
1890
            $objFont->setSize($size / 20);
1891
1892
            // offset: 2; size: 2; option flags
1893
            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
1894
            // bit: 1; mask 0x0002; italic
1895
            $isItalic = (0x0002 & self::getInt2d($recordData, 2)) >> 1;
1896
            if ($isItalic) {
1897
                $objFont->setItalic(true);
1898
            }
1899
1900
            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
1901
            // bit: 3; mask 0x0008; strike
1902
            $isStrike = (0x0008 & self::getInt2d($recordData, 2)) >> 3;
1903
            if ($isStrike) {
1904
                $objFont->setStrikethrough(true);
1905
            }
1906
1907
            // offset: 4; size: 2; colour index
1908
            $colorIndex = self::getInt2d($recordData, 4);
1909
            $objFont->colorIndex = $colorIndex;
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in 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...
1910
1911
            // offset: 6; size: 2; font weight
1912
            $weight = self::getInt2d($recordData, 6);
1913
            switch ($weight) {
1914
                case 0x02BC:
1915
                    $objFont->setBold(true);
1916
                    break;
1917
            }
1918
1919
            // offset: 8; size: 2; escapement type
1920
            $escapement = self::getInt2d($recordData, 8);
1921
            switch ($escapement) {
1922
                case 0x0001:
1923
                    $objFont->setSuperScript(true);
1924
                    break;
1925
                case 0x0002:
1926
                    $objFont->setSubScript(true);
1927
                    break;
1928
            }
1929
1930
            // offset: 10; size: 1; underline type
1931
            $underlineType = ord($recordData{10});
1932
            switch ($underlineType) {
1933
                case 0x00:
1934
                    break; // no underline
1935
                case 0x01:
1936
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1937
                    break;
1938
                case 0x02:
1939
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE);
1940
                    break;
1941
                case 0x21:
1942
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING);
1943
                    break;
1944
                case 0x22:
1945
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING);
1946
                    break;
1947
            }
1948
1949
            // offset: 11; size: 1; font family
1950
            // offset: 12; size: 1; character set
1951
            // offset: 13; size: 1; not used
1952
            // offset: 14; size: var; font name
1953 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...
1954
                $string = self::readUnicodeStringShort(substr($recordData, 14));
1955
            } else {
1956
                $string = $this->readByteStringShort(substr($recordData, 14));
1957
            }
1958
            $objFont->setName($string['value']);
1959
1960
            $this->objFonts[] = $objFont;
1961
        }
1962
    }
1963
1964
    /**
1965
     * FORMAT
1966
     *
1967
     * This record contains information about a number format.
1968
     * All FORMAT records occur together in a sequential list.
1969
     *
1970
     * In BIFF2-BIFF4 other records referencing a FORMAT record
1971
     * contain a zero-based index into this list. From BIFF5 on
1972
     * the FORMAT record contains the index itself that will be
1973
     * used by other records.
1974
     *
1975
     * --    "OpenOffice.org's Documentation of the Microsoft
1976
     *         Excel File Format"
1977
     */
1978
    private function readFormat()
1979
    {
1980
        $length = self::getInt2d($this->data, $this->pos + 2);
1981
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1982
1983
        // move stream pointer to next record
1984
        $this->pos += 4 + $length;
1985
1986
        if (!$this->readDataOnly) {
1987
            $indexCode = self::getInt2d($recordData, 0);
1988
1989 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...
1990
                $string = self::readUnicodeStringLong(substr($recordData, 2));
1991
            } else {
1992
                // BIFF7
1993
                $string = $this->readByteStringShort(substr($recordData, 2));
1994
            }
1995
1996
            $formatString = $string['value'];
1997
            $this->formats[$indexCode] = $formatString;
1998
        }
1999
    }
2000
2001
    /**
2002
     * XF - Extended Format
2003
     *
2004
     * This record contains formatting information for cells, rows, columns or styles.
2005
     * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
2006
     * and 1 cell XF.
2007
     * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
2008
     * and XF record 15 is a cell XF
2009
     * We only read the first cell style XF and skip the remaining cell style XF records
2010
     * We read all cell XF records.
2011
     *
2012
     * --    "OpenOffice.org's Documentation of the Microsoft
2013
     *         Excel File Format"
2014
     */
2015
    private function readXf()
2016
    {
2017
        $length = self::getInt2d($this->data, $this->pos + 2);
2018
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2019
2020
        // move stream pointer to next record
2021
        $this->pos += 4 + $length;
2022
2023
        $objStyle = new \PhpSpreadsheet\Style();
2024
2025
        if (!$this->readDataOnly) {
2026
            // offset:  0; size: 2; Index to FONT record
2027
            if (self::getInt2d($recordData, 0) < 4) {
2028
                $fontIndex = self::getInt2d($recordData, 0);
2029
            } else {
2030
                // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
2031
                // check the OpenOffice documentation of the FONT record
2032
                $fontIndex = self::getInt2d($recordData, 0) - 1;
2033
            }
2034
            $objStyle->setFont($this->objFonts[$fontIndex]);
2035
2036
            // offset:  2; size: 2; Index to FORMAT record
2037
            $numberFormatIndex = self::getInt2d($recordData, 2);
2038
            if (isset($this->formats[$numberFormatIndex])) {
2039
                // then we have user-defined format code
2040
                $numberformat = ['code' => $this->formats[$numberFormatIndex]];
2041
            } elseif (($code = \PhpSpreadsheet\Style\NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
2042
                // then we have built-in format code
2043
                $numberformat = ['code' => $code];
2044
            } else {
2045
                // we set the general format code
2046
                $numberformat = ['code' => 'General'];
2047
            }
2048
            $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
2049
2050
            // offset:  4; size: 2; XF type, cell protection, and parent style XF
2051
            // 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...
2052
            $xfTypeProt = self::getInt2d($recordData, 4);
2053
            // bit 0; mask 0x01; 1 = cell is locked
2054
            $isLocked = (0x01 & $xfTypeProt) >> 0;
2055
            $objStyle->getProtection()->setLocked($isLocked ? \PhpSpreadsheet\Style\Protection::PROTECTION_INHERIT : \PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2056
2057
            // bit 1; mask 0x02; 1 = Formula is hidden
2058
            $isHidden = (0x02 & $xfTypeProt) >> 1;
2059
            $objStyle->getProtection()->setHidden($isHidden ? \PhpSpreadsheet\Style\Protection::PROTECTION_PROTECTED : \PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2060
2061
            // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
2062
            $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
2063
2064
            // offset:  6; size: 1; Alignment and text break
2065
            // bit 2-0, mask 0x07; horizontal alignment
2066
            $horAlign = (0x07 & ord($recordData{6})) >> 0;
2067
            switch ($horAlign) {
2068
                case 0:
2069
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL);
2070
                    break;
2071
                case 1:
2072
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
2073
                    break;
2074
                case 2:
2075
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
2076
                    break;
2077
                case 3:
2078
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT);
2079
                    break;
2080
                case 4:
2081
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_FILL);
2082
                    break;
2083
                case 5:
2084
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY);
2085
                    break;
2086
                case 6:
2087
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS);
2088
                    break;
2089
            }
2090
            // bit 3, mask 0x08; wrap text
2091
            $wrapText = (0x08 & ord($recordData{6})) >> 3;
2092
            switch ($wrapText) {
2093
                case 0:
2094
                    $objStyle->getAlignment()->setWrapText(false);
2095
                    break;
2096
                case 1:
2097
                    $objStyle->getAlignment()->setWrapText(true);
2098
                    break;
2099
            }
2100
            // bit 6-4, mask 0x70; vertical alignment
2101
            $vertAlign = (0x70 & ord($recordData{6})) >> 4;
2102
            switch ($vertAlign) {
2103
                case 0:
2104
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP);
2105
                    break;
2106
                case 1:
2107
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
2108
                    break;
2109
                case 2:
2110
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM);
2111
                    break;
2112
                case 3:
2113
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_JUSTIFY);
2114
                    break;
2115
            }
2116
2117
            if ($this->version == self::XLS_BIFF8) {
2118
                // offset:  7; size: 1; XF_ROTATION: Text rotation angle
2119
                $angle = ord($recordData{7});
2120
                $rotation = 0;
2121
                if ($angle <= 90) {
2122
                    $rotation = $angle;
2123
                } elseif ($angle <= 180) {
2124
                    $rotation = 90 - $angle;
2125
                } elseif ($angle == 255) {
2126
                    $rotation = -165;
2127
                }
2128
                $objStyle->getAlignment()->setTextRotation($rotation);
2129
2130
                // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
2131
                // bit: 3-0; mask: 0x0F; indent level
2132
                $indent = (0x0F & ord($recordData{8})) >> 0;
2133
                $objStyle->getAlignment()->setIndent($indent);
2134
2135
                // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
2136
                $shrinkToFit = (0x10 & ord($recordData{8})) >> 4;
2137
                switch ($shrinkToFit) {
2138
                    case 0:
2139
                        $objStyle->getAlignment()->setShrinkToFit(false);
2140
                        break;
2141
                    case 1:
2142
                        $objStyle->getAlignment()->setShrinkToFit(true);
2143
                        break;
2144
                }
2145
2146
                // offset:  9; size: 1; Flags used for attribute groups
2147
2148
                // offset: 10; size: 4; Cell border lines and background area
2149
                // bit: 3-0; mask: 0x0000000F; left style
2150 View Code Duplication
                if ($bordersLeftStyle = Excel5\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...
2151
                    $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
2152
                }
2153
                // bit: 7-4; mask: 0x000000F0; right style
2154 View Code Duplication
                if ($bordersRightStyle = Excel5\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...
2155
                    $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
2156
                }
2157
                // bit: 11-8; mask: 0x00000F00; top style
2158 View Code Duplication
                if ($bordersTopStyle = Excel5\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...
2159
                    $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
2160
                }
2161
                // bit: 15-12; mask: 0x0000F000; bottom style
2162 View Code Duplication
                if ($bordersBottomStyle = Excel5\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...
2163
                    $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
2164
                }
2165
                // bit: 22-16; mask: 0x007F0000; left color
2166
                $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 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...
2167
2168
                // bit: 29-23; mask: 0x3F800000; right color
2169
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2170
2171
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2172
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2173
2174
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2175
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2176
2177
                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...
2178
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_NONE);
2179
                } 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...
2180
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_UP);
2181
                } 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...
2182
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_DOWN);
2183
                } 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...
2184
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_BOTH);
2185
                }
2186
2187
                // 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...
2188
                // bit: 6-0; mask: 0x0000007F; top color
2189
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2190
2191
                // bit: 13-7; mask: 0x00003F80; bottom color
2192
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2193
2194
                // bit: 20-14; mask: 0x001FC000; diagonal color
2195
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2196
2197
                // bit: 24-21; mask: 0x01E00000; diagonal style
2198 View Code Duplication
                if ($bordersDiagonalStyle = Excel5\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...
2199
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2200
                }
2201
2202
                // bit: 31-26; mask: 0xFC000000 fill pattern
2203
                if ($fillType = Excel5\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2204
                    $objStyle->getFill()->setFillType($fillType);
2205
                }
2206
                // offset: 18; size: 2; pattern and background colour
2207
                // bit: 6-0; mask: 0x007F; color index for pattern color
2208
                $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...
2209
2210
                // bit: 13-7; mask: 0x3F80; color index for pattern background
2211
                $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...
2212
            } else {
2213
                // BIFF5
2214
2215
                // offset: 7; size: 1; Text orientation and flags
2216
                $orientationAndFlags = ord($recordData{7});
2217
2218
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2219
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2220
                switch ($xfOrientation) {
2221
                    case 0:
2222
                        $objStyle->getAlignment()->setTextRotation(0);
2223
                        break;
2224
                    case 1:
2225
                        $objStyle->getAlignment()->setTextRotation(-165);
2226
                        break;
2227
                    case 2:
2228
                        $objStyle->getAlignment()->setTextRotation(90);
2229
                        break;
2230
                    case 3:
2231
                        $objStyle->getAlignment()->setTextRotation(-90);
2232
                        break;
2233
                }
2234
2235
                // offset: 8; size: 4; cell border lines and background area
2236
                $borderAndBackground = self::getInt4d($recordData, 8);
2237
2238
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2239
                $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...
2240
2241
                // bit: 13-7; mask: 0x00003F80; color index for pattern background
2242
                $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...
2243
2244
                // bit: 21-16; mask: 0x003F0000; fill pattern
2245
                $objStyle->getFill()->setFillType(Excel5\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2246
2247
                // bit: 24-22; mask: 0x01C00000; bottom line style
2248
                $objStyle->getBorders()->getBottom()->setBorderStyle(Excel5\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2249
2250
                // bit: 31-25; mask: 0xFE000000; bottom line color
2251
                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2252
2253
                // offset: 12; size: 4; cell border lines
2254
                $borderLines = self::getInt4d($recordData, 12);
2255
2256
                // bit: 2-0; mask: 0x00000007; top line style
2257
                $objStyle->getBorders()->getTop()->setBorderStyle(Excel5\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2258
2259
                // bit: 5-3; mask: 0x00000038; left line style
2260
                $objStyle->getBorders()->getLeft()->setBorderStyle(Excel5\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2261
2262
                // bit: 8-6; mask: 0x000001C0; right line style
2263
                $objStyle->getBorders()->getRight()->setBorderStyle(Excel5\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2264
2265
                // bit: 15-9; mask: 0x0000FE00; top line color index
2266
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2267
2268
                // bit: 22-16; mask: 0x007F0000; left line color index
2269
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2270
2271
                // bit: 29-23; mask: 0x3F800000; right line color index
2272
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2273
            }
2274
2275
            // add cellStyleXf or cellXf and update mapping
2276
            if ($isCellStyleXf) {
2277
                // we only read one style XF record which is always the first
2278
                if ($this->xfIndex == 0) {
2279
                    $this->spreadsheet->addCellStyleXf($objStyle);
2280
                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
2281
                }
2282
            } else {
2283
                // we read all cell XF records
2284
                $this->spreadsheet->addCellXf($objStyle);
2285
                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2286
            }
2287
2288
            // update XF index for when we read next record
2289
            ++$this->xfIndex;
2290
        }
2291
    }
2292
2293
    /**
2294
     *
2295
     */
2296
    private function readXfExt()
2297
    {
2298
        $length = self::getInt2d($this->data, $this->pos + 2);
2299
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2300
2301
        // move stream pointer to next record
2302
        $this->pos += 4 + $length;
2303
2304
        if (!$this->readDataOnly) {
2305
            // offset: 0; size: 2; 0x087D = repeated header
2306
2307
            // offset: 2; size: 2
2308
2309
            // offset: 4; size: 8; not used
2310
2311
            // offset: 12; size: 2; record version
2312
2313
            // offset: 14; size: 2; index to XF record which this record modifies
2314
            $ixfe = self::getInt2d($recordData, 14);
2315
2316
            // offset: 16; size: 2; not used
2317
2318
            // offset: 18; size: 2; number of extension properties that follow
2319
            $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...
2320
2321
            // start reading the actual extension data
2322
            $offset = 20;
2323
            while ($offset < $length) {
2324
                // extension type
2325
                $extType = self::getInt2d($recordData, $offset);
2326
2327
                // extension length
2328
                $cb = self::getInt2d($recordData, $offset + 2);
2329
2330
                // extension data
2331
                $extData = substr($recordData, $offset + 4, $cb);
2332
2333
                switch ($extType) {
2334 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...
2335
                        $xclfType = self::getInt2d($extData, 0); // color type
2336
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2337
2338
                        if ($xclfType == 2) {
2339
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2340
2341
                            // modify the relevant style property
2342
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2343
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2344
                                $fill->getStartColor()->setRGB($rgb);
2345
                                unset($fill->startcolorIndex); // normal color index does not apply, discard
2346
                            }
2347
                        }
2348
                        break;
2349 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...
2350
                        $xclfType = self::getInt2d($extData, 0); // color type
2351
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2352
2353
                        if ($xclfType == 2) {
2354
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2355
2356
                            // modify the relevant style property
2357
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2358
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2359
                                $fill->getEndColor()->setRGB($rgb);
2360
                                unset($fill->endcolorIndex); // normal color index does not apply, discard
2361
                            }
2362
                        }
2363
                        break;
2364 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...
2365
                        $xclfType = self::getInt2d($extData, 0); // color type
2366
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2367
2368
                        if ($xclfType == 2) {
2369
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2370
2371
                            // modify the relevant style property
2372
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2373
                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2374
                                $top->getColor()->setRGB($rgb);
2375
                                unset($top->colorIndex); // normal color index does not apply, discard
2376
                            }
2377
                        }
2378
                        break;
2379 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...
2380
                        $xclfType = self::getInt2d($extData, 0); // color type
2381
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2382
2383
                        if ($xclfType == 2) {
2384
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2385
2386
                            // modify the relevant style property
2387
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2388
                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2389
                                $bottom->getColor()->setRGB($rgb);
2390
                                unset($bottom->colorIndex); // normal color index does not apply, discard
2391
                            }
2392
                        }
2393
                        break;
2394 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...
2395
                        $xclfType = self::getInt2d($extData, 0); // color type
2396
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2397
2398
                        if ($xclfType == 2) {
2399
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2400
2401
                            // modify the relevant style property
2402
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2403
                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2404
                                $left->getColor()->setRGB($rgb);
2405
                                unset($left->colorIndex); // normal color index does not apply, discard
2406
                            }
2407
                        }
2408
                        break;
2409 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...
2410
                        $xclfType = self::getInt2d($extData, 0); // color type
2411
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2412
2413
                        if ($xclfType == 2) {
2414
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2415
2416
                            // modify the relevant style property
2417
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2418
                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2419
                                $right->getColor()->setRGB($rgb);
2420
                                unset($right->colorIndex); // normal color index does not apply, discard
2421
                            }
2422
                        }
2423
                        break;
2424 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...
2425
                        $xclfType = self::getInt2d($extData, 0); // color type
2426
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2427
2428
                        if ($xclfType == 2) {
2429
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2430
2431
                            // modify the relevant style property
2432
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2433
                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2434
                                $diagonal->getColor()->setRGB($rgb);
2435
                                unset($diagonal->colorIndex); // normal color index does not apply, discard
2436
                            }
2437
                        }
2438
                        break;
2439 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...
2440
                        $xclfType = self::getInt2d($extData, 0); // color type
2441
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2442
2443
                        if ($xclfType == 2) {
2444
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2445
2446
                            // modify the relevant style property
2447
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2448
                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2449
                                $font->getColor()->setRGB($rgb);
2450
                                unset($font->colorIndex); // normal color index does not apply, discard
2451
                            }
2452
                        }
2453
                        break;
2454
                }
2455
2456
                $offset += $cb;
2457
            }
2458
        }
2459
    }
2460
2461
    /**
2462
     * Read STYLE record
2463
     */
2464
    private function readStyle()
2465
    {
2466
        $length = self::getInt2d($this->data, $this->pos + 2);
2467
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2468
2469
        // move stream pointer to next record
2470
        $this->pos += 4 + $length;
2471
2472
        if (!$this->readDataOnly) {
2473
            // offset: 0; size: 2; index to XF record and flag for built-in style
2474
            $ixfe = self::getInt2d($recordData, 0);
2475
2476
            // bit: 11-0; mask 0x0FFF; index to XF record
2477
            $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...
2478
2479
            // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
2480
            $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
2481
2482
            if ($isBuiltIn) {
2483
                // offset: 2; size: 1; identifier for built-in style
2484
                $builtInId = ord($recordData{2});
2485
2486
                switch ($builtInId) {
2487
                    case 0x00:
2488
                        // currently, we are not using this for anything
2489
                        break;
2490
                    default:
2491
                        break;
2492
                }
2493
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
2494
                // user-defined; not supported by PhpSpreadsheet
2495
            }
2496
        }
2497
    }
2498
2499
    /**
2500
     * Read PALETTE record
2501
     */
2502
    private function readPalette()
2503
    {
2504
        $length = self::getInt2d($this->data, $this->pos + 2);
2505
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2506
2507
        // move stream pointer to next record
2508
        $this->pos += 4 + $length;
2509
2510
        if (!$this->readDataOnly) {
2511
            // offset: 0; size: 2; number of following colors
2512
            $nm = self::getInt2d($recordData, 0);
2513
2514
            // list of RGB colors
2515
            for ($i = 0; $i < $nm; ++$i) {
2516
                $rgb = substr($recordData, 2 + 4 * $i, 4);
2517
                $this->palette[] = self::readRGB($rgb);
2518
            }
2519
        }
2520
    }
2521
2522
    /**
2523
     * SHEET
2524
     *
2525
     * This record is  located in the  Workbook Globals
2526
     * Substream  and represents a sheet inside the workbook.
2527
     * One SHEET record is written for each sheet. It stores the
2528
     * sheet name and a stream offset to the BOF record of the
2529
     * respective Sheet Substream within the Workbook Stream.
2530
     *
2531
     * --    "OpenOffice.org's Documentation of the Microsoft
2532
     *         Excel File Format"
2533
     */
2534
    private function readSheet()
2535
    {
2536
        $length = self::getInt2d($this->data, $this->pos + 2);
2537
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2538
2539
        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
2540
        // NOTE: not encrypted
2541
        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
2542
2543
        // move stream pointer to next record
2544
        $this->pos += 4 + $length;
2545
2546
        // offset: 4; size: 1; sheet state
2547
        switch (ord($recordData{4})) {
2548
            case 0x00:
2549
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2550
                break;
2551
            case 0x01:
2552
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_HIDDEN;
2553
                break;
2554
            case 0x02:
2555
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VERYHIDDEN;
2556
                break;
2557
            default:
2558
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2559
                break;
2560
        }
2561
2562
        // offset: 5; size: 1; sheet type
2563
        $sheetType = ord($recordData{5});
2564
2565
        // offset: 6; size: var; sheet name
2566 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...
2567
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2568
            $rec_name = $string['value'];
2569
        } elseif ($this->version == self::XLS_BIFF7) {
2570
            $string = $this->readByteStringShort(substr($recordData, 6));
2571
            $rec_name = $string['value'];
2572
        }
2573
2574
        $this->sheets[] = [
2575
            '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...
2576
            'offset' => $rec_offset,
2577
            'sheetState' => $sheetState,
2578
            'sheetType' => $sheetType,
2579
        ];
2580
    }
2581
2582
    /**
2583
     * Read EXTERNALBOOK record
2584
     */
2585
    private function readExternalBook()
2586
    {
2587
        $length = self::getInt2d($this->data, $this->pos + 2);
2588
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2589
2590
        // move stream pointer to next record
2591
        $this->pos += 4 + $length;
2592
2593
        // offset within record data
2594
        $offset = 0;
2595
2596
        // there are 4 types of records
2597
        if (strlen($recordData) > 4) {
2598
            // external reference
2599
            // offset: 0; size: 2; number of sheet names ($nm)
2600
            $nm = self::getInt2d($recordData, 0);
2601
            $offset += 2;
2602
2603
            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
2604
            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2605
            $offset += $encodedUrlString['size'];
2606
2607
            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
2608
            $externalSheetNames = [];
2609
            for ($i = 0; $i < $nm; ++$i) {
2610
                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2611
                $externalSheetNames[] = $externalSheetNameString['value'];
2612
                $offset += $externalSheetNameString['size'];
2613
            }
2614
2615
            // store the record data
2616
            $this->externalBooks[] = [
2617
                'type' => 'external',
2618
                'encodedUrl' => $encodedUrlString['value'],
2619
                'externalSheetNames' => $externalSheetNames,
2620
            ];
2621
        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
2622
            // internal reference
2623
            // offset: 0; size: 2; number of sheet in this document
2624
            // 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...
2625
            $this->externalBooks[] = [
2626
                'type' => 'internal',
2627
            ];
2628
        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
2629
            // add-in function
2630
            // 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...
2631
            $this->externalBooks[] = [
2632
                'type' => 'addInFunction',
2633
            ];
2634
        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
2635
            // DDE links, OLE links
2636
            // 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...
2637
            // offset: 2; size: var; encoded source document name
2638
            $this->externalBooks[] = [
2639
                'type' => 'DDEorOLE',
2640
            ];
2641
        }
2642
    }
2643
2644
    /**
2645
     * Read EXTERNNAME record.
2646
     */
2647
    private function readExternName()
2648
    {
2649
        $length = self::getInt2d($this->data, $this->pos + 2);
2650
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2651
2652
        // move stream pointer to next record
2653
        $this->pos += 4 + $length;
2654
2655
        // external sheet references provided for named cells
2656
        if ($this->version == self::XLS_BIFF8) {
2657
            // offset: 0; size: 2; options
2658
            $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...
2659
2660
            // 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...
2661
2662
            // offset: 4; size: 2; not used
2663
2664
            // offset: 6; size: var
2665
            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2666
2667
            // offset: var; size: var; formula data
2668
            $offset = 6 + $nameString['size'];
2669
            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
2670
2671
            $this->externalNames[] = [
2672
                'name' => $nameString['value'],
2673
                'formula' => $formula,
2674
            ];
2675
        }
2676
    }
2677
2678
    /**
2679
     * Read EXTERNSHEET record
2680
     */
2681
    private function readExternSheet()
2682
    {
2683
        $length = self::getInt2d($this->data, $this->pos + 2);
2684
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2685
2686
        // move stream pointer to next record
2687
        $this->pos += 4 + $length;
2688
2689
        // external sheet references provided for named cells
2690
        if ($this->version == self::XLS_BIFF8) {
2691
            // offset: 0; size: 2; number of following ref structures
2692
            $nm = self::getInt2d($recordData, 0);
2693
            for ($i = 0; $i < $nm; ++$i) {
2694
                $this->ref[] = [
2695
                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
2696
                    'externalBookIndex' => self::getInt2d($recordData, 2 + 6 * $i),
2697
                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
2698
                    'firstSheetIndex' => self::getInt2d($recordData, 4 + 6 * $i),
2699
                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
2700
                    'lastSheetIndex' => self::getInt2d($recordData, 6 + 6 * $i),
2701
                ];
2702
            }
2703
        }
2704
    }
2705
2706
    /**
2707
     * DEFINEDNAME
2708
     *
2709
     * This record is part of a Link Table. It contains the name
2710
     * and the token array of an internal defined name. Token
2711
     * arrays of defined names contain tokens with aberrant
2712
     * token classes.
2713
     *
2714
     * --    "OpenOffice.org's Documentation of the Microsoft
2715
     *         Excel File Format"
2716
     */
2717
    private function readDefinedName()
2718
    {
2719
        $length = self::getInt2d($this->data, $this->pos + 2);
2720
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2721
2722
        // move stream pointer to next record
2723
        $this->pos += 4 + $length;
2724
2725
        if ($this->version == self::XLS_BIFF8) {
2726
            // retrieves named cells
2727
2728
            // offset: 0; size: 2; option flags
2729
            $opts = self::getInt2d($recordData, 0);
2730
2731
            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
2732
            $isBuiltInName = (0x0020 & $opts) >> 5;
2733
2734
            // offset: 2; size: 1; keyboard shortcut
2735
2736
            // offset: 3; size: 1; length of the name (character count)
2737
            $nlen = ord($recordData{3});
2738
2739
            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
2740
            // note: there can also be additional data, this is not included in $flen
2741
            $flen = self::getInt2d($recordData, 4);
2742
2743
            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
2744
            $scope = self::getInt2d($recordData, 8);
2745
2746
            // offset: 14; size: var; Name (Unicode string without length field)
2747
            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2748
2749
            // offset: var; size: $flen; formula data
2750
            $offset = 14 + $string['size'];
2751
            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
2752
2753
            try {
2754
                $formula = $this->getFormulaFromStructure($formulaStructure);
2755
            } catch (\PhpSpreadsheet\Exception $e) {
2756
                $formula = '';
2757
            }
2758
2759
            $this->definedname[] = [
2760
                'isBuiltInName' => $isBuiltInName,
2761
                'name' => $string['value'],
2762
                'formula' => $formula,
2763
                'scope' => $scope,
2764
            ];
2765
        }
2766
    }
2767
2768
    /**
2769
     * Read MSODRAWINGGROUP record
2770
     */
2771 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...
2772
    {
2773
        $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...
2774
2775
        // get spliced record data
2776
        $splicedRecordData = $this->getSplicedRecordData();
2777
        $recordData = $splicedRecordData['recordData'];
2778
2779
        $this->drawingGroupData .= $recordData;
2780
    }
2781
2782
    /**
2783
     * SST - Shared String Table
2784
     *
2785
     * This record contains a list of all strings used anywhere
2786
     * in the workbook. Each string occurs only once. The
2787
     * workbook uses indexes into the list to reference the
2788
     * strings.
2789
     *
2790
     * --    "OpenOffice.org's Documentation of the Microsoft
2791
     *         Excel File Format"
2792
     **/
2793
    private function readSst()
2794
    {
2795
        // offset within (spliced) record data
2796
        $pos = 0;
2797
2798
        // get spliced record data
2799
        $splicedRecordData = $this->getSplicedRecordData();
2800
2801
        $recordData = $splicedRecordData['recordData'];
2802
        $spliceOffsets = $splicedRecordData['spliceOffsets'];
2803
2804
        // offset: 0; size: 4; total number of strings in the workbook
2805
        $pos += 4;
2806
2807
        // offset: 4; size: 4; number of following strings ($nm)
2808
        $nm = self::getInt4d($recordData, 4);
2809
        $pos += 4;
2810
2811
        // loop through the Unicode strings (16-bit length)
2812
        for ($i = 0; $i < $nm; ++$i) {
2813
            // number of characters in the Unicode string
2814
            $numChars = self::getInt2d($recordData, $pos);
2815
            $pos += 2;
2816
2817
            // option flags
2818
            $optionFlags = ord($recordData{$pos});
2819
            ++$pos;
2820
2821
            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
2822
            $isCompressed = (($optionFlags & 0x01) == 0);
2823
2824
            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
2825
            $hasAsian = (($optionFlags & 0x04) != 0);
2826
2827
            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
2828
            $hasRichText = (($optionFlags & 0x08) != 0);
2829
2830
            if ($hasRichText) {
2831
                // number of Rich-Text formatting runs
2832
                $formattingRuns = self::getInt2d($recordData, $pos);
2833
                $pos += 2;
2834
            }
2835
2836
            if ($hasAsian) {
2837
                // size of Asian phonetic setting
2838
                $extendedRunLength = self::getInt4d($recordData, $pos);
2839
                $pos += 4;
2840
            }
2841
2842
            // expected byte length of character array if not split
2843
            $len = ($isCompressed) ? $numChars : $numChars * 2;
2844
2845
            // look up limit position
2846
            foreach ($spliceOffsets as $spliceOffset) {
2847
                // it can happen that the string is empty, therefore we need
2848
                // <= and not just <
2849
                if ($pos <= $spliceOffset) {
2850
                    $limitpos = $spliceOffset;
2851
                    break;
2852
                }
2853
            }
2854
2855
            if ($pos + $len <= $limitpos) {
2856
                // character array is not split between records
2857
2858
                $retstr = substr($recordData, $pos, $len);
2859
                $pos += $len;
2860
            } else {
2861
                // character array is split between records
2862
2863
                // first part of character array
2864
                $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...
2865
2866
                $bytesRead = $limitpos - $pos;
2867
2868
                // remaining characters in Unicode string
2869
                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2870
2871
                $pos = $limitpos;
2872
2873
                // keep reading the characters
2874
                while ($charsLeft > 0) {
2875
                    // look up next limit position, in case the string span more than one continue record
2876
                    foreach ($spliceOffsets as $spliceOffset) {
2877
                        if ($pos < $spliceOffset) {
2878
                            $limitpos = $spliceOffset;
2879
                            break;
2880
                        }
2881
                    }
2882
2883
                    // repeated option flags
2884
                    // OpenOffice.org documentation 5.21
2885
                    $option = ord($recordData{$pos});
2886
                    ++$pos;
2887
2888
                    if ($isCompressed && ($option == 0)) {
2889
                        // 1st fragment compressed
2890
                        // this fragment compressed
2891
                        $len = min($charsLeft, $limitpos - $pos);
2892
                        $retstr .= substr($recordData, $pos, $len);
2893
                        $charsLeft -= $len;
2894
                        $isCompressed = true;
2895
                    } elseif (!$isCompressed && ($option != 0)) {
2896
                        // 1st fragment uncompressed
2897
                        // this fragment uncompressed
2898
                        $len = min($charsLeft * 2, $limitpos - $pos);
2899
                        $retstr .= substr($recordData, $pos, $len);
2900
                        $charsLeft -= $len / 2;
2901
                        $isCompressed = false;
2902
                    } elseif (!$isCompressed && ($option == 0)) {
2903
                        // 1st fragment uncompressed
2904
                        // this fragment compressed
2905
                        $len = min($charsLeft, $limitpos - $pos);
2906
                        for ($j = 0; $j < $len; ++$j) {
2907
                            $retstr .= $recordData{$pos + $j}
2908
                            . chr(0);
2909
                        }
2910
                        $charsLeft -= $len;
2911
                        $isCompressed = false;
2912
                    } else {
2913
                        // 1st fragment compressed
2914
                        // this fragment uncompressed
2915
                        $newstr = '';
2916
                        for ($j = 0; $j < strlen($retstr); ++$j) {
2917
                            $newstr .= $retstr[$j] . chr(0);
2918
                        }
2919
                        $retstr = $newstr;
2920
                        $len = min($charsLeft * 2, $limitpos - $pos);
2921
                        $retstr .= substr($recordData, $pos, $len);
2922
                        $charsLeft -= $len / 2;
2923
                        $isCompressed = false;
2924
                    }
2925
2926
                    $pos += $len;
2927
                }
2928
            }
2929
2930
            // convert to UTF-8
2931
            $retstr = self::encodeUTF16($retstr, $isCompressed);
2932
2933
            // read additional Rich-Text information, if any
2934
            $fmtRuns = [];
2935
            if ($hasRichText) {
2936
                // list of formatting runs
2937
                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...
2938
                    // first formatted character; zero-based
2939
                    $charPos = self::getInt2d($recordData, $pos + $j * 4);
2940
2941
                    // index to font record
2942
                    $fontIndex = self::getInt2d($recordData, $pos + 2 + $j * 4);
2943
2944
                    $fmtRuns[] = [
2945
                        'charPos' => $charPos,
2946
                        'fontIndex' => $fontIndex,
2947
                    ];
2948
                }
2949
                $pos += 4 * $formattingRuns;
2950
            }
2951
2952
            // read additional Asian phonetics information, if any
2953
            if ($hasAsian) {
2954
                // For Asian phonetic settings, we skip the extended string data
2955
                $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...
2956
            }
2957
2958
            // store the shared sting
2959
            $this->sst[] = [
2960
                'value' => $retstr,
2961
                'fmtRuns' => $fmtRuns,
2962
            ];
2963
        }
2964
2965
        // getSplicedRecordData() takes care of moving current position in data stream
2966
    }
2967
2968
    /**
2969
     * Read PRINTGRIDLINES record
2970
     */
2971
    private function readPrintGridlines()
2972
    {
2973
        $length = self::getInt2d($this->data, $this->pos + 2);
2974
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2975
2976
        // move stream pointer to next record
2977
        $this->pos += 4 + $length;
2978
2979
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
2980
            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
2981
            $printGridlines = (bool) self::getInt2d($recordData, 0);
2982
            $this->phpSheet->setPrintGridlines($printGridlines);
2983
        }
2984
    }
2985
2986
    /**
2987
     * Read DEFAULTROWHEIGHT record
2988
     */
2989 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...
2990
    {
2991
        $length = self::getInt2d($this->data, $this->pos + 2);
2992
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2993
2994
        // move stream pointer to next record
2995
        $this->pos += 4 + $length;
2996
2997
        // offset: 0; size: 2; option flags
2998
        // 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...
2999
        $height = self::getInt2d($recordData, 2);
3000
        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3001
    }
3002
3003
    /**
3004
     * Read SHEETPR record
3005
     */
3006
    private function readSheetPr()
3007
    {
3008
        $length = self::getInt2d($this->data, $this->pos + 2);
3009
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3010
3011
        // move stream pointer to next record
3012
        $this->pos += 4 + $length;
3013
3014
        // offset: 0; size: 2
3015
3016
        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
3017
        $isSummaryBelow = (0x0040 & self::getInt2d($recordData, 0)) >> 6;
3018
        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
3019
3020
        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
3021
        $isSummaryRight = (0x0080 & self::getInt2d($recordData, 0)) >> 7;
3022
        $this->phpSheet->setShowSummaryRight($isSummaryRight);
3023
3024
        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
3025
        // this corresponds to radio button setting in page setup dialog in Excel
3026
        $this->isFitToPages = (bool) ((0x0100 & self::getInt2d($recordData, 0)) >> 8);
3027
    }
3028
3029
    /**
3030
     * Read HORIZONTALPAGEBREAKS record
3031
     */
3032 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...
3033
    {
3034
        $length = self::getInt2d($this->data, $this->pos + 2);
3035
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3036
3037
        // move stream pointer to next record
3038
        $this->pos += 4 + $length;
3039
3040
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3041
            // offset: 0; size: 2; number of the following row index structures
3042
            $nm = self::getInt2d($recordData, 0);
3043
3044
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3045
            for ($i = 0; $i < $nm; ++$i) {
3046
                $r = self::getInt2d($recordData, 2 + 6 * $i);
3047
                $cf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3048
                $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...
3049
3050
                // not sure why two column indexes are necessary?
3051
                $this->phpSheet->setBreakByColumnAndRow($cf, $r, \PhpSpreadsheet\Worksheet::BREAK_ROW);
3052
            }
3053
        }
3054
    }
3055
3056
    /**
3057
     * Read VERTICALPAGEBREAKS record
3058
     */
3059 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...
3060
    {
3061
        $length = self::getInt2d($this->data, $this->pos + 2);
3062
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3063
3064
        // move stream pointer to next record
3065
        $this->pos += 4 + $length;
3066
3067
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3068
            // offset: 0; size: 2; number of the following column index structures
3069
            $nm = self::getInt2d($recordData, 0);
3070
3071
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3072
            for ($i = 0; $i < $nm; ++$i) {
3073
                $c = self::getInt2d($recordData, 2 + 6 * $i);
3074
                $rf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3075
                $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...
3076
3077
                // not sure why two row indexes are necessary?
3078
                $this->phpSheet->setBreakByColumnAndRow($c, $rf, \PhpSpreadsheet\Worksheet::BREAK_COLUMN);
3079
            }
3080
        }
3081
    }
3082
3083
    /**
3084
     * Read HEADER record
3085
     */
3086 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...
3087
    {
3088
        $length = self::getInt2d($this->data, $this->pos + 2);
3089
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3090
3091
        // move stream pointer to next record
3092
        $this->pos += 4 + $length;
3093
3094
        if (!$this->readDataOnly) {
3095
            // offset: 0; size: var
3096
            // realized that $recordData can be empty even when record exists
3097
            if ($recordData) {
3098
                if ($this->version == self::XLS_BIFF8) {
3099
                    $string = self::readUnicodeStringLong($recordData);
3100
                } else {
3101
                    $string = $this->readByteStringShort($recordData);
3102
                }
3103
3104
                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
3105
                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
3106
            }
3107
        }
3108
    }
3109
3110
    /**
3111
     * Read FOOTER record
3112
     */
3113 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...
3114
    {
3115
        $length = self::getInt2d($this->data, $this->pos + 2);
3116
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3117
3118
        // move stream pointer to next record
3119
        $this->pos += 4 + $length;
3120
3121
        if (!$this->readDataOnly) {
3122
            // offset: 0; size: var
3123
            // realized that $recordData can be empty even when record exists
3124
            if ($recordData) {
3125
                if ($this->version == self::XLS_BIFF8) {
3126
                    $string = self::readUnicodeStringLong($recordData);
3127
                } else {
3128
                    $string = $this->readByteStringShort($recordData);
3129
                }
3130
                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
3131
                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
3132
            }
3133
        }
3134
    }
3135
3136
    /**
3137
     * Read HCENTER record
3138
     */
3139 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...
3140
    {
3141
        $length = self::getInt2d($this->data, $this->pos + 2);
3142
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3143
3144
        // move stream pointer to next record
3145
        $this->pos += 4 + $length;
3146
3147
        if (!$this->readDataOnly) {
3148
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3149
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3150
3151
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3152
        }
3153
    }
3154
3155
    /**
3156
     * Read VCENTER record
3157
     */
3158 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...
3159
    {
3160
        $length = self::getInt2d($this->data, $this->pos + 2);
3161
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3162
3163
        // move stream pointer to next record
3164
        $this->pos += 4 + $length;
3165
3166
        if (!$this->readDataOnly) {
3167
            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
3168
            $isVerticalCentered = (bool) self::getInt2d($recordData, 0);
3169
3170
            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3171
        }
3172
    }
3173
3174
    /**
3175
     * Read LEFTMARGIN record
3176
     */
3177
    private function readLeftMargin()
3178
    {
3179
        $length = self::getInt2d($this->data, $this->pos + 2);
3180
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3181
3182
        // move stream pointer to next record
3183
        $this->pos += 4 + $length;
3184
3185
        if (!$this->readDataOnly) {
3186
            // offset: 0; size: 8
3187
            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3188
        }
3189
    }
3190
3191
    /**
3192
     * Read RIGHTMARGIN record
3193
     */
3194
    private function readRightMargin()
3195
    {
3196
        $length = self::getInt2d($this->data, $this->pos + 2);
3197
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3198
3199
        // move stream pointer to next record
3200
        $this->pos += 4 + $length;
3201
3202
        if (!$this->readDataOnly) {
3203
            // offset: 0; size: 8
3204
            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3205
        }
3206
    }
3207
3208
    /**
3209
     * Read TOPMARGIN record
3210
     */
3211
    private function readTopMargin()
3212
    {
3213
        $length = self::getInt2d($this->data, $this->pos + 2);
3214
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3215
3216
        // move stream pointer to next record
3217
        $this->pos += 4 + $length;
3218
3219
        if (!$this->readDataOnly) {
3220
            // offset: 0; size: 8
3221
            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3222
        }
3223
    }
3224
3225
    /**
3226
     * Read BOTTOMMARGIN record
3227
     */
3228
    private function readBottomMargin()
3229
    {
3230
        $length = self::getInt2d($this->data, $this->pos + 2);
3231
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3232
3233
        // move stream pointer to next record
3234
        $this->pos += 4 + $length;
3235
3236
        if (!$this->readDataOnly) {
3237
            // offset: 0; size: 8
3238
            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3239
        }
3240
    }
3241
3242
    /**
3243
     * Read PAGESETUP record
3244
     */
3245
    private function readPageSetup()
3246
    {
3247
        $length = self::getInt2d($this->data, $this->pos + 2);
3248
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3249
3250
        // move stream pointer to next record
3251
        $this->pos += 4 + $length;
3252
3253
        if (!$this->readDataOnly) {
3254
            // offset: 0; size: 2; paper size
3255
            $paperSize = self::getInt2d($recordData, 0);
3256
3257
            // offset: 2; size: 2; scaling factor
3258
            $scale = self::getInt2d($recordData, 2);
3259
3260
            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
3261
            $fitToWidth = self::getInt2d($recordData, 6);
3262
3263
            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
3264
            $fitToHeight = self::getInt2d($recordData, 8);
3265
3266
            // offset: 10; size: 2; option flags
3267
3268
            // 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...
3269
            $isPortrait = (0x0002 & self::getInt2d($recordData, 10)) >> 1;
3270
3271
            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
3272
            // when this bit is set, do not use flags for those properties
3273
            $isNotInit = (0x0004 & self::getInt2d($recordData, 10)) >> 2;
3274
3275
            if (!$isNotInit) {
3276
                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3277
                switch ($isPortrait) {
3278
                    case 0:
3279
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
3280
                        break;
3281
                    case 1:
3282
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
3283
                        break;
3284
                }
3285
3286
                $this->phpSheet->getPageSetup()->setScale($scale, false);
3287
                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
3288
                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
3289
                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
3290
            }
3291
3292
            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
3293
            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3294
            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3295
3296
            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
3297
            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3298
            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3299
        }
3300
    }
3301
3302
    /**
3303
     * PROTECT - Sheet protection (BIFF2 through BIFF8)
3304
     *   if this record is omitted, then it also means no sheet protection
3305
     */
3306 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...
3307
    {
3308
        $length = self::getInt2d($this->data, $this->pos + 2);
3309
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3310
3311
        // move stream pointer to next record
3312
        $this->pos += 4 + $length;
3313
3314
        if ($this->readDataOnly) {
3315
            return;
3316
        }
3317
3318
        // 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...
3319
3320
        // bit 0, mask 0x01; 1 = sheet is protected
3321
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3322
        $this->phpSheet->getProtection()->setSheet((bool) $bool);
3323
    }
3324
3325
    /**
3326
     * SCENPROTECT
3327
     */
3328 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...
3329
    {
3330
        $length = self::getInt2d($this->data, $this->pos + 2);
3331
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3332
3333
        // move stream pointer to next record
3334
        $this->pos += 4 + $length;
3335
3336
        if ($this->readDataOnly) {
3337
            return;
3338
        }
3339
3340
        // 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...
3341
3342
        // bit: 0, mask 0x01; 1 = scenarios are protected
3343
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3344
3345
        $this->phpSheet->getProtection()->setScenarios((bool) $bool);
3346
    }
3347
3348
    /**
3349
     * OBJECTPROTECT
3350
     */
3351 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...
3352
    {
3353
        $length = self::getInt2d($this->data, $this->pos + 2);
3354
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3355
3356
        // move stream pointer to next record
3357
        $this->pos += 4 + $length;
3358
3359
        if ($this->readDataOnly) {
3360
            return;
3361
        }
3362
3363
        // 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...
3364
3365
        // bit: 0, mask 0x01; 1 = objects are protected
3366
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3367
3368
        $this->phpSheet->getProtection()->setObjects((bool) $bool);
3369
    }
3370
3371
    /**
3372
     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
3373
     */
3374
    private function readPassword()
3375
    {
3376
        $length = self::getInt2d($this->data, $this->pos + 2);
3377
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3378
3379
        // move stream pointer to next record
3380
        $this->pos += 4 + $length;
3381
3382
        if (!$this->readDataOnly) {
3383
            // offset: 0; size: 2; 16-bit hash value of password
3384
            $password = strtoupper(dechex(self::getInt2d($recordData, 0))); // the hashed password
3385
            $this->phpSheet->getProtection()->setPassword($password, true);
3386
        }
3387
    }
3388
3389
    /**
3390
     * Read DEFCOLWIDTH record
3391
     */
3392 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...
3393
    {
3394
        $length = self::getInt2d($this->data, $this->pos + 2);
3395
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3396
3397
        // move stream pointer to next record
3398
        $this->pos += 4 + $length;
3399
3400
        // offset: 0; size: 2; default column width
3401
        $width = self::getInt2d($recordData, 0);
3402
        if ($width != 8) {
3403
            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3404
        }
3405
    }
3406
3407
    /**
3408
     * Read COLINFO record
3409
     */
3410
    private function readColInfo()
3411
    {
3412
        $length = self::getInt2d($this->data, $this->pos + 2);
3413
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3414
3415
        // move stream pointer to next record
3416
        $this->pos += 4 + $length;
3417
3418
        if (!$this->readDataOnly) {
3419
            // offset: 0; size: 2; index to first column in range
3420
            $fc = self::getInt2d($recordData, 0); // first column index
3421
3422
            // offset: 2; size: 2; index to last column in range
3423
            $lc = self::getInt2d($recordData, 2); // first column index
3424
3425
            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
3426
            $width = self::getInt2d($recordData, 4);
3427
3428
            // offset: 6; size: 2; index to XF record for default column formatting
3429
            $xfIndex = self::getInt2d($recordData, 6);
3430
3431
            // offset: 8; size: 2; option flags
3432
            // bit: 0; mask: 0x0001; 1= columns are hidden
3433
            $isHidden = (0x0001 & self::getInt2d($recordData, 8)) >> 0;
3434
3435
            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
3436
            $level = (0x0700 & self::getInt2d($recordData, 8)) >> 8;
3437
3438
            // bit: 12; mask: 0x1000; 1 = collapsed
3439
            $isCollapsed = (0x1000 & self::getInt2d($recordData, 8)) >> 12;
3440
3441
            // offset: 10; size: 2; not used
3442
3443
            for ($i = $fc; $i <= $lc; ++$i) {
3444
                if ($lc == 255 || $lc == 256) {
3445
                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3446
                    break;
3447
                }
3448
                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
3449
                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
3450
                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
3451
                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
3452
                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3453
            }
3454
        }
3455
    }
3456
3457
    /**
3458
     * ROW
3459
     *
3460
     * This record contains the properties of a single row in a
3461
     * sheet. Rows and cells in a sheet are divided into blocks
3462
     * of 32 rows.
3463
     *
3464
     * --    "OpenOffice.org's Documentation of the Microsoft
3465
     *         Excel File Format"
3466
     */
3467
    private function readRow()
3468
    {
3469
        $length = self::getInt2d($this->data, $this->pos + 2);
3470
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3471
3472
        // move stream pointer to next record
3473
        $this->pos += 4 + $length;
3474
3475
        if (!$this->readDataOnly) {
3476
            // offset: 0; size: 2; index of this row
3477
            $r = self::getInt2d($recordData, 0);
3478
3479
            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
3480
3481
            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
3482
3483
            // 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...
3484
3485
            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
3486
            $height = (0x7FFF & self::getInt2d($recordData, 6)) >> 0;
3487
3488
            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
3489
            $useDefaultHeight = (0x8000 & self::getInt2d($recordData, 6)) >> 15;
3490
3491
            if (!$useDefaultHeight) {
3492
                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
3493
            }
3494
3495
            // offset: 8; size: 2; not used
3496
3497
            // offset: 10; size: 2; not used in BIFF5-BIFF8
3498
3499
            // offset: 12; size: 4; option flags and default row formatting
3500
3501
            // bit: 2-0: mask: 0x00000007; outline level of the row
3502
            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3503
            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
3504
3505
            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
3506
            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
3507
            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
3508
3509
            // bit: 5; mask: 0x00000020; 1 = row is hidden
3510
            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3511
            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
3512
3513
            // bit: 7; mask: 0x00000080; 1 = row has explicit format
3514
            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3515
3516
            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
3517
            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3518
3519
            if ($hasExplicitFormat) {
3520
                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3521
            }
3522
        }
3523
    }
3524
3525
    /**
3526
     * Read RK record
3527
     * This record represents a cell that contains an RK value
3528
     * (encoded integer or floating-point value). If a
3529
     * floating-point value cannot be encoded to an RK value,
3530
     * a NUMBER record will be written. This record replaces the
3531
     * record INTEGER written in BIFF2.
3532
     *
3533
     * --    "OpenOffice.org's Documentation of the Microsoft
3534
     *         Excel File Format"
3535
     */
3536 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...
3537
    {
3538
        $length = self::getInt2d($this->data, $this->pos + 2);
3539
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3540
3541
        // move stream pointer to next record
3542
        $this->pos += 4 + $length;
3543
3544
        // offset: 0; size: 2; index to row
3545
        $row = self::getInt2d($recordData, 0);
3546
3547
        // offset: 2; size: 2; index to column
3548
        $column = self::getInt2d($recordData, 2);
3549
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3550
3551
        // Read cell?
3552
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3553
            // offset: 4; size: 2; index to XF record
3554
            $xfIndex = self::getInt2d($recordData, 4);
3555
3556
            // offset: 6; size: 4; RK value
3557
            $rknum = self::getInt4d($recordData, 6);
3558
            $numValue = self::getIEEE754($rknum);
3559
3560
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3561
            if (!$this->readDataOnly) {
3562
                // add style information
3563
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3564
            }
3565
3566
            // add cell
3567
            $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3568
        }
3569
    }
3570
3571
    /**
3572
     * Read LABELSST record
3573
     * This record represents a cell that contains a string. It
3574
     * replaces the LABEL record and RSTRING record used in
3575
     * BIFF2-BIFF5.
3576
     *
3577
     * --    "OpenOffice.org's Documentation of the Microsoft
3578
     *         Excel File Format"
3579
     */
3580
    private function readLabelSst()
3581
    {
3582
        $length = self::getInt2d($this->data, $this->pos + 2);
3583
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3584
3585
        // move stream pointer to next record
3586
        $this->pos += 4 + $length;
3587
3588
        // offset: 0; size: 2; index to row
3589
        $row = self::getInt2d($recordData, 0);
3590
3591
        // offset: 2; size: 2; index to column
3592
        $column = self::getInt2d($recordData, 2);
3593
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3594
3595
        $emptyCell = true;
3596
        // Read cell?
3597
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3598
            // offset: 4; size: 2; index to XF record
3599
            $xfIndex = self::getInt2d($recordData, 4);
3600
3601
            // offset: 6; size: 4; index to SST record
3602
            $index = self::getInt4d($recordData, 6);
3603
3604
            // add cell
3605
            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
3606
                // then we should treat as rich text
3607
                $richText = new \PhpSpreadsheet\RichText();
3608
                $charPos = 0;
3609
                $sstCount = count($this->sst[$index]['fmtRuns']);
3610
                for ($i = 0; $i <= $sstCount; ++$i) {
3611
                    if (isset($fmtRuns[$i])) {
3612
                        $text = \PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
3613
                        $charPos = $fmtRuns[$i]['charPos'];
3614
                    } else {
3615
                        $text = \PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, \PhpSpreadsheet\Shared\StringHelper::countCharacters($this->sst[$index]['value']));
3616
                    }
3617
3618
                    if (\PhpSpreadsheet\Shared\StringHelper::countCharacters($text) > 0) {
3619
                        if ($i == 0) { // first text run, no style
3620
                            $richText->createText($text);
3621
                        } else {
3622
                            $textRun = $richText->createTextRun($text);
3623
                            if (isset($fmtRuns[$i - 1])) {
3624
                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
3625
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
3626
                                } else {
3627
                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
3628
                                    // check the OpenOffice documentation of the FONT record
3629
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
3630
                                }
3631
                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
3632
                            }
3633
                        }
3634
                    }
3635
                }
3636
                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
3637
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3638
                    $cell->setValueExplicit($richText, \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3639
                    $emptyCell = false;
3640
                }
3641
            } else {
3642
                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
3643
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3644
                    $cell->setValueExplicit($this->sst[$index]['value'], \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3645
                    $emptyCell = false;
3646
                }
3647
            }
3648
3649
            if (!$this->readDataOnly && !$emptyCell) {
3650
                // add style information
3651
                $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...
3652
            }
3653
        }
3654
    }
3655
3656
    /**
3657
     * Read MULRK record
3658
     * This record represents a cell range containing RK value
3659
     * cells. All cells are located in the same row.
3660
     *
3661
     * --    "OpenOffice.org's Documentation of the Microsoft
3662
     *         Excel File Format"
3663
     */
3664
    private function readMulRk()
3665
    {
3666
        $length = self::getInt2d($this->data, $this->pos + 2);
3667
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3668
3669
        // move stream pointer to next record
3670
        $this->pos += 4 + $length;
3671
3672
        // offset: 0; size: 2; index to row
3673
        $row = self::getInt2d($recordData, 0);
3674
3675
        // offset: 2; size: 2; index to first column
3676
        $colFirst = self::getInt2d($recordData, 2);
3677
3678
        // offset: var; size: 2; index to last column
3679
        $colLast = self::getInt2d($recordData, $length - 2);
3680
        $columns = $colLast - $colFirst + 1;
3681
3682
        // offset within record data
3683
        $offset = 4;
3684
3685
        for ($i = 0; $i < $columns; ++$i) {
3686
            $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($colFirst + $i);
3687
3688
            // Read cell?
3689
            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3690
                // offset: var; size: 2; index to XF record
3691
                $xfIndex = self::getInt2d($recordData, $offset);
3692
3693
                // offset: var; size: 4; RK value
3694
                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3695
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3696
                if (!$this->readDataOnly) {
3697
                    // add style
3698
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3699
                }
3700
3701
                // add cell value
3702
                $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3703
            }
3704
3705
            $offset += 6;
3706
        }
3707
    }
3708
3709
    /**
3710
     * Read NUMBER record
3711
     * This record represents a cell that contains a
3712
     * floating-point value.
3713
     *
3714
     * --    "OpenOffice.org's Documentation of the Microsoft
3715
     *         Excel File Format"
3716
     */
3717 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...
3718
    {
3719
        $length = self::getInt2d($this->data, $this->pos + 2);
3720
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3721
3722
        // move stream pointer to next record
3723
        $this->pos += 4 + $length;
3724
3725
        // offset: 0; size: 2; index to row
3726
        $row = self::getInt2d($recordData, 0);
3727
3728
        // offset: 2; size 2; index to column
3729
        $column = self::getInt2d($recordData, 2);
3730
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3731
3732
        // Read cell?
3733
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3734
            // offset 4; size: 2; index to XF record
3735
            $xfIndex = self::getInt2d($recordData, 4);
3736
3737
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3738
3739
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3740
            if (!$this->readDataOnly) {
3741
                // add cell style
3742
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3743
            }
3744
3745
            // add cell value
3746
            $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3747
        }
3748
    }
3749
3750
    /**
3751
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3752
     * This record contains the token array and the result of a
3753
     * formula cell.
3754
     *
3755
     * --    "OpenOffice.org's Documentation of the Microsoft
3756
     *         Excel File Format"
3757
     */
3758
    private function readFormula()
3759
    {
3760
        $length = self::getInt2d($this->data, $this->pos + 2);
3761
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3762
3763
        // move stream pointer to next record
3764
        $this->pos += 4 + $length;
3765
3766
        // offset: 0; size: 2; row index
3767
        $row = self::getInt2d($recordData, 0);
3768
3769
        // offset: 2; size: 2; col index
3770
        $column = self::getInt2d($recordData, 2);
3771
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3772
3773
        // offset: 20: size: variable; formula structure
3774
        $formulaStructure = substr($recordData, 20);
3775
3776
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3777
        $options = self::getInt2d($recordData, 14);
3778
3779
        // bit: 0; mask: 0x0001; 1 = recalculate always
3780
        // bit: 1; mask: 0x0002; 1 = calculate on open
3781
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3782
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3783
3784
        // WARNING:
3785
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3786
        // the formula data may be ordinary formula data, therefore we need to check
3787
        // explicitly for the tExp token (0x01)
3788
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01;
3789
3790
        if ($isPartOfSharedFormula) {
3791
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3792
            // get the base cell, grab tExp token
3793
            $baseRow = self::getInt2d($formulaStructure, 3);
3794
            $baseCol = self::getInt2d($formulaStructure, 5);
3795
            $this->_baseCell = \PhpSpreadsheet\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...
3796
        }
3797
3798
        // Read cell?
3799
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3800
            if ($isPartOfSharedFormula) {
3801
                // formula is added to this cell after the sheet has been read
3802
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3803
            }
3804
3805
            // offset: 16: size: 4; not used
3806
3807
            // offset: 4; size: 2; XF index
3808
            $xfIndex = self::getInt2d($recordData, 4);
3809
3810
            // offset: 6; size: 8; result of the formula
3811
            if ((ord($recordData{6}) == 0) && (ord($recordData{12}) == 255) && (ord($recordData{13}) == 255)) {
3812
                // String formula. Result follows in appended STRING record
3813
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_STRING;
3814
3815
                // read possible SHAREDFMLA record
3816
                $code = self::getInt2d($this->data, $this->pos);
3817
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3818
                    $this->readSharedFmla();
3819
                }
3820
3821
                // read STRING record
3822
                $value = $this->readString();
3823 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...
3824
                && (ord($recordData{12}) == 255)
3825
                && (ord($recordData{13}) == 255)) {
3826
                // Boolean formula. Result is in +2; 0=false, 1=true
3827
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
3828
                $value = (bool) ord($recordData{8});
3829
            } elseif ((ord($recordData{6}) == 2)
3830
                && (ord($recordData{12}) == 255)
3831
                && (ord($recordData{13}) == 255)) {
3832
                // Error formula. Error code is in +2
3833
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
3834
                $value = Excel5\ErrorCode::lookup(ord($recordData{8}));
3835 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...
3836
                && (ord($recordData{12}) == 255)
3837
                && (ord($recordData{13}) == 255)) {
3838
                // Formula result is a null string
3839
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_NULL;
3840
                $value = '';
3841
            } else {
3842
                // forumla result is a number, first 14 bytes like _NUMBER record
3843
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
3844
                $value = self::extractNumber(substr($recordData, 6, 8));
3845
            }
3846
3847
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3848
            if (!$this->readDataOnly) {
3849
                // add cell style
3850
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3851
            }
3852
3853
            // store the formula
3854
            if (!$isPartOfSharedFormula) {
3855
                // not part of shared formula
3856
                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
3857
                try {
3858
                    if ($this->version != self::XLS_BIFF8) {
3859
                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
3860
                    }
3861
                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
3862
                    $cell->setValueExplicit('=' . $formula, \PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
3863
                } catch (\PhpSpreadsheet\Exception $e) {
3864
                    $cell->setValueExplicit($value, $dataType);
3865
                }
3866
            } else {
3867
                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...
3868
                    // do nothing at this point, formula id added later in the code
3869
                } else {
3870
                    $cell->setValueExplicit($value, $dataType);
3871
                }
3872
            }
3873
3874
            // store the cached calculated value
3875
            $cell->setCalculatedValue($value);
3876
        }
3877
    }
3878
3879
    /**
3880
     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
3881
     * which usually contains relative references.
3882
     * These will be used to construct the formula in each shared formula part after the sheet is read.
3883
     */
3884
    private function readSharedFmla()
3885
    {
3886
        $length = self::getInt2d($this->data, $this->pos + 2);
3887
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3888
3889
        // move stream pointer to next record
3890
        $this->pos += 4 + $length;
3891
3892
        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
3893
        $cellRange = substr($recordData, 0, 6);
3894
        $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...
3895
3896
        // offset: 6, size: 1; not used
3897
3898
        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
3899
        $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...
3900
3901
        // offset: 8, size: var; Binary token array of the shared formula
3902
        $formula = substr($recordData, 8);
3903
3904
        // at this point we only store the shared formula for later use
3905
        $this->sharedFormulas[$this->_baseCell] = $formula;
3906
    }
3907
3908
    /**
3909
     * Read a STRING record from current stream position and advance the stream pointer to next record
3910
     * This record is used for storing result from FORMULA record when it is a string, and
3911
     * it occurs directly after the FORMULA record
3912
     *
3913
     * @return string The string contents as UTF-8
3914
     */
3915
    private function readString()
3916
    {
3917
        $length = self::getInt2d($this->data, $this->pos + 2);
3918
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3919
3920
        // move stream pointer to next record
3921
        $this->pos += 4 + $length;
3922
3923
        if ($this->version == self::XLS_BIFF8) {
3924
            $string = self::readUnicodeStringLong($recordData);
3925
            $value = $string['value'];
3926
        } else {
3927
            $string = $this->readByteStringLong($recordData);
3928
            $value = $string['value'];
3929
        }
3930
3931
        return $value;
3932
    }
3933
3934
    /**
3935
     * Read BOOLERR record
3936
     * This record represents a Boolean value or error value
3937
     * cell.
3938
     *
3939
     * --    "OpenOffice.org's Documentation of the Microsoft
3940
     *         Excel File Format"
3941
     */
3942
    private function readBoolErr()
3943
    {
3944
        $length = self::getInt2d($this->data, $this->pos + 2);
3945
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3946
3947
        // move stream pointer to next record
3948
        $this->pos += 4 + $length;
3949
3950
        // offset: 0; size: 2; row index
3951
        $row = self::getInt2d($recordData, 0);
3952
3953
        // offset: 2; size: 2; column index
3954
        $column = self::getInt2d($recordData, 2);
3955
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3956
3957
        // Read cell?
3958
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3959
            // offset: 4; size: 2; index to XF record
3960
            $xfIndex = self::getInt2d($recordData, 4);
3961
3962
            // offset: 6; size: 1; the boolean value or error value
3963
            $boolErr = ord($recordData{6});
3964
3965
            // 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...
3966
            $isError = ord($recordData{7});
3967
3968
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3969
            switch ($isError) {
3970
                case 0: // boolean
3971
                    $value = (bool) $boolErr;
3972
3973
                    // add cell value
3974
                    $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_BOOL);
3975
                    break;
3976
                case 1: // error type
3977
                    $value = Excel5\ErrorCode::lookup($boolErr);
3978
3979
                    // add cell value
3980
                    $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_ERROR);
3981
                    break;
3982
            }
3983
3984
            if (!$this->readDataOnly) {
3985
                // add cell style
3986
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3987
            }
3988
        }
3989
    }
3990
3991
    /**
3992
     * Read MULBLANK record
3993
     * This record represents a cell range of empty cells. All
3994
     * cells are located in the same row
3995
     *
3996
     * --    "OpenOffice.org's Documentation of the Microsoft
3997
     *         Excel File Format"
3998
     */
3999
    private function readMulBlank()
4000
    {
4001
        $length = self::getInt2d($this->data, $this->pos + 2);
4002
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4003
4004
        // move stream pointer to next record
4005
        $this->pos += 4 + $length;
4006
4007
        // offset: 0; size: 2; index to row
4008
        $row = self::getInt2d($recordData, 0);
4009
4010
        // offset: 2; size: 2; index to first column
4011
        $fc = self::getInt2d($recordData, 2);
4012
4013
        // offset: 4; size: 2 x nc; list of indexes to XF records
4014
        // add style information
4015
        if (!$this->readDataOnly && $this->readEmptyCells) {
4016
            for ($i = 0; $i < $length / 2 - 3; ++$i) {
4017
                $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc + $i);
4018
4019
                // Read cell?
4020
                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4021
                    $xfIndex = self::getInt2d($recordData, 4 + 2 * $i);
4022
                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4023
                }
4024
            }
4025
        }
4026
4027
        // offset: 6; size 2; index to last column (not needed)
4028
    }
4029
4030
    /**
4031
     * Read LABEL record
4032
     * This record represents a cell that contains a string. In
4033
     * BIFF8 it is usually replaced by the LABELSST record.
4034
     * Excel still uses this record, if it copies unformatted
4035
     * text cells to the clipboard.
4036
     *
4037
     * --    "OpenOffice.org's Documentation of the Microsoft
4038
     *         Excel File Format"
4039
     */
4040
    private function readLabel()
4041
    {
4042
        $length = self::getInt2d($this->data, $this->pos + 2);
4043
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4044
4045
        // move stream pointer to next record
4046
        $this->pos += 4 + $length;
4047
4048
        // offset: 0; size: 2; index to row
4049
        $row = self::getInt2d($recordData, 0);
4050
4051
        // offset: 2; size: 2; index to column
4052
        $column = self::getInt2d($recordData, 2);
4053
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
4054
4055
        // Read cell?
4056
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4057
            // offset: 4; size: 2; XF index
4058
            $xfIndex = self::getInt2d($recordData, 4);
4059
4060
            // add cell value
4061
            // todo: what if string is very long? continue record
4062 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...
4063
                $string = self::readUnicodeStringLong(substr($recordData, 6));
4064
                $value = $string['value'];
4065
            } else {
4066
                $string = $this->readByteStringLong(substr($recordData, 6));
4067
                $value = $string['value'];
4068
            }
4069
            if ($this->readEmptyCells || trim($value) !== '') {
4070
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4071
                $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
4072
4073
                if (!$this->readDataOnly) {
4074
                    // add cell style
4075
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4076
                }
4077
            }
4078
        }
4079
    }
4080
4081
    /**
4082
     * Read BLANK record
4083
     */
4084
    private function readBlank()
4085
    {
4086
        $length = self::getInt2d($this->data, $this->pos + 2);
4087
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4088
4089
        // move stream pointer to next record
4090
        $this->pos += 4 + $length;
4091
4092
        // offset: 0; size: 2; row index
4093
        $row = self::getInt2d($recordData, 0);
4094
4095
        // offset: 2; size: 2; col index
4096
        $col = self::getInt2d($recordData, 2);
4097
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($col);
4098
4099
        // Read cell?
4100
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4101
            // offset: 4; size: 2; XF index
4102
            $xfIndex = self::getInt2d($recordData, 4);
4103
4104
            // add style information
4105
            if (!$this->readDataOnly && $this->readEmptyCells) {
4106
                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4107
            }
4108
        }
4109
    }
4110
4111
    /**
4112
     * Read MSODRAWING record
4113
     */
4114 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...
4115
    {
4116
        $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...
4117
4118
        // get spliced record data
4119
        $splicedRecordData = $this->getSplicedRecordData();
4120
        $recordData = $splicedRecordData['recordData'];
4121
4122
        $this->drawingData .= $recordData;
4123
    }
4124
4125
    /**
4126
     * Read OBJ record
4127
     */
4128
    private function readObj()
4129
    {
4130
        $length = self::getInt2d($this->data, $this->pos + 2);
4131
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4132
4133
        // move stream pointer to next record
4134
        $this->pos += 4 + $length;
4135
4136
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4137
            return;
4138
        }
4139
4140
        // recordData consists of an array of subrecords looking like this:
4141
        //    ft: 2 bytes; ftCmo type (0x15)
4142
        //    cb: 2 bytes; size in bytes of ftCmo data
4143
        //    ot: 2 bytes; Object Type
4144
        //    id: 2 bytes; Object id number
4145
        //    grbit: 2 bytes; Option Flags
4146
        //    data: var; subrecord data
4147
4148
        // for now, we are just interested in the second subrecord containing the object type
4149
        $ftCmoType = self::getInt2d($recordData, 0);
4150
        $cbCmoSize = self::getInt2d($recordData, 2);
4151
        $otObjType = self::getInt2d($recordData, 4);
4152
        $idObjID = self::getInt2d($recordData, 6);
4153
        $grbitOpts = self::getInt2d($recordData, 6);
4154
4155
        $this->objs[] = [
4156
            'ftCmoType' => $ftCmoType,
4157
            'cbCmoSize' => $cbCmoSize,
4158
            'otObjType' => $otObjType,
4159
            'idObjID' => $idObjID,
4160
            'grbitOpts' => $grbitOpts,
4161
        ];
4162
        $this->textObjRef = $idObjID;
4163
    }
4164
4165
    /**
4166
     * Read WINDOW2 record
4167
     */
4168
    private function readWindow2()
4169
    {
4170
        $length = self::getInt2d($this->data, $this->pos + 2);
4171
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4172
4173
        // move stream pointer to next record
4174
        $this->pos += 4 + $length;
4175
4176
        // offset: 0; size: 2; option flags
4177
        $options = self::getInt2d($recordData, 0);
4178
4179
        // offset: 2; size: 2; index to first visible row
4180
        $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...
4181
4182
        // offset: 4; size: 2; index to first visible colum
4183
        $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...
4184
        if ($this->version === self::XLS_BIFF8) {
4185
            // offset:  8; size: 2; not used
4186
            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
4187
            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
4188
            // offset: 14; size: 4; not used
4189
            $zoomscaleInPageBreakPreview = self::getInt2d($recordData, 10);
4190
            if ($zoomscaleInPageBreakPreview === 0) {
4191
                $zoomscaleInPageBreakPreview = 60;
4192
            }
4193
            $zoomscaleInNormalView = self::getInt2d($recordData, 12);
4194
            if ($zoomscaleInNormalView === 0) {
4195
                $zoomscaleInNormalView = 100;
4196
            }
4197
        }
4198
4199
        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
4200
        $showGridlines = (bool) ((0x0002 & $options) >> 1);
4201
        $this->phpSheet->setShowGridlines($showGridlines);
4202
4203
        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
4204
        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4205
        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4206
4207
        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
4208
        $this->frozen = (bool) ((0x0008 & $options) >> 3);
4209
4210
        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
4211
        $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
4212
4213
        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
4214
        $isActive = (bool) ((0x0400 & $options) >> 10);
4215
        if ($isActive) {
4216
            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4217
        }
4218
4219
        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
4220
        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4221
4222
        //FIXME: set $firstVisibleRow and $firstVisibleColumn
4223
4224
        if ($this->phpSheet->getSheetView()->getView() !== \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT) {
4225
            //NOTE: this setting is inferior to page layout view(Excel2007-)
4226
            $view = $isPageBreakPreview ? \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_NORMAL;
4227
            $this->phpSheet->getSheetView()->setView($view);
4228
            if ($this->version === self::XLS_BIFF8) {
4229
                $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...
4230
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4231
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4232
            }
4233
        }
4234
    }
4235
4236
    /**
4237
     * Read PLV Record(Created by Excel2007 or upper)
4238
     */
4239
    private function readPageLayoutView()
4240
    {
4241
        $length = self::getInt2d($this->data, $this->pos + 2);
4242
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4243
4244
        // move stream pointer to next record
4245
        $this->pos += 4 + $length;
4246
4247
        // offset: 0; size: 2; rt
4248
        //->ignore
4249
        $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...
4250
        // offset: 2; size: 2; grbitfr
4251
        //->ignore
4252
        $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...
4253
        // offset: 4; size: 8; reserved
4254
        //->ignore
4255
4256
        // offset: 12; size 2; zoom scale
4257
        $wScalePLV = self::getInt2d($recordData, 12);
4258
        // offset: 14; size 2; grbit
4259
        $grbit = self::getInt2d($recordData, 14);
4260
4261
        // decomprise grbit
4262
        $fPageLayoutView = $grbit & 0x01;
4263
        $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...
4264
        $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...
4265
4266
        if ($fPageLayoutView === 1) {
4267
            $this->phpSheet->getSheetView()->setView(\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT);
4268
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4269
        }
4270
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4271
    }
4272
4273
    /**
4274
     * Read SCL record
4275
     */
4276
    private function readScl()
4277
    {
4278
        $length = self::getInt2d($this->data, $this->pos + 2);
4279
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4280
4281
        // move stream pointer to next record
4282
        $this->pos += 4 + $length;
4283
4284
        // offset: 0; size: 2; numerator of the view magnification
4285
        $numerator = self::getInt2d($recordData, 0);
4286
4287
        // offset: 2; size: 2; numerator of the view magnification
4288
        $denumerator = self::getInt2d($recordData, 2);
4289
4290
        // set the zoom scale (in percent)
4291
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4292
    }
4293
4294
    /**
4295
     * Read PANE record
4296
     */
4297
    private function readPane()
4298
    {
4299
        $length = self::getInt2d($this->data, $this->pos + 2);
4300
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4301
4302
        // move stream pointer to next record
4303
        $this->pos += 4 + $length;
4304
4305
        if (!$this->readDataOnly) {
4306
            // offset: 0; size: 2; position of vertical split
4307
            $px = self::getInt2d($recordData, 0);
4308
4309
            // offset: 2; size: 2; position of horizontal split
4310
            $py = self::getInt2d($recordData, 2);
4311
4312
            if ($this->frozen) {
4313
                // frozen panes
4314
                $this->phpSheet->freezePane(\PhpSpreadsheet\Cell::stringFromColumnIndex($px) . ($py + 1));
4315
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
4316
                // unfrozen panes; split windows; not supported by PhpSpreadsheet core
4317
            }
4318
        }
4319
    }
4320
4321
    /**
4322
     * Read SELECTION record. There is one such record for each pane in the sheet.
4323
     */
4324
    private function readSelection()
4325
    {
4326
        $length = self::getInt2d($this->data, $this->pos + 2);
4327
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4328
4329
        // move stream pointer to next record
4330
        $this->pos += 4 + $length;
4331
4332
        if (!$this->readDataOnly) {
4333
            // offset: 0; size: 1; pane identifier
4334
            $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...
4335
4336
            // offset: 1; size: 2; index to row of the active cell
4337
            $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...
4338
4339
            // offset: 3; size: 2; index to column of the active cell
4340
            $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...
4341
4342
            // offset: 5; size: 2; index into the following cell range list to the
4343
            //  entry that contains the active cell
4344
            $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...
4345
4346
            // offset: 7; size: var; cell range address list containing all selected cell ranges
4347
            $data = substr($recordData, 7);
4348
            $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
4349
4350
            $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
4351
4352
            // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
4353
            if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
4354
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
4355
            }
4356
4357
            // first row '1' + last row '65536' indicates that full column is selected
4358
            if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
4359
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
4360
            }
4361
4362
            // first column 'A' + last column 'IV' indicates that full row is selected
4363
            if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
4364
                $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
4365
            }
4366
4367
            $this->phpSheet->setSelectedCells($selectedCells);
4368
        }
4369
    }
4370
4371
    private function includeCellRangeFiltered($cellRangeAddress)
4372
    {
4373
        $includeCellRange = true;
4374
        if ($this->getReadFilter() !== null) {
4375
            $includeCellRange = false;
4376
            $rangeBoundaries = \PhpSpreadsheet\Cell::getRangeBoundaries($cellRangeAddress);
4377
            ++$rangeBoundaries[1][0];
4378
            for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
4379
                for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
4380
                    if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
4381
                        $includeCellRange = true;
4382
                        break 2;
4383
                    }
4384
                }
4385
            }
4386
        }
4387
4388
        return $includeCellRange;
4389
    }
4390
4391
    /**
4392
     * MERGEDCELLS
4393
     *
4394
     * This record contains the addresses of merged cell ranges
4395
     * in the current sheet.
4396
     *
4397
     * --    "OpenOffice.org's Documentation of the Microsoft
4398
     *         Excel File Format"
4399
     */
4400
    private function readMergedCells()
4401
    {
4402
        $length = self::getInt2d($this->data, $this->pos + 2);
4403
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4404
4405
        // move stream pointer to next record
4406
        $this->pos += 4 + $length;
4407
4408
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
4409
            $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
4410
            foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
4411
                if ((strpos($cellRangeAddress, ':') !== false) &&
4412
                    ($this->includeCellRangeFiltered($cellRangeAddress))) {
4413
                    $this->phpSheet->mergeCells($cellRangeAddress);
4414
                }
4415
            }
4416
        }
4417
    }
4418
4419
    /**
4420
     * Read HYPERLINK record
4421
     */
4422
    private function readHyperLink()
4423
    {
4424
        $length = self::getInt2d($this->data, $this->pos + 2);
4425
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4426
4427
        // move stream pointer forward to next record
4428
        $this->pos += 4 + $length;
4429
4430
        if (!$this->readDataOnly) {
4431
            // offset: 0; size: 8; cell range address of all cells containing this hyperlink
4432
            try {
4433
                $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData, 0, 8);
0 ignored issues
show
Unused Code introduced by
The call to Excel5::readBIFF8CellRangeAddressFixed() has too many arguments starting with 0.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4434
            } catch (\PhpSpreadsheet\Exception $e) {
4435
                return;
4436
            }
4437
4438
            // offset: 8, size: 16; GUID of StdLink
4439
4440
            // offset: 24, size: 4; unknown value
4441
4442
            // offset: 28, size: 4; option flags
4443
            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
4444
            $isFileLinkOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 0;
4445
4446
            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
4447
            $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...
4448
4449
            // 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...
4450
            $hasDesc = (0x00000014 & self::getInt2d($recordData, 28)) >> 2;
4451
4452
            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
4453
            $hasText = (0x00000008 & self::getInt2d($recordData, 28)) >> 3;
4454
4455
            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
4456
            $hasFrame = (0x00000080 & self::getInt2d($recordData, 28)) >> 7;
4457
4458
            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
4459
            $isUNC = (0x00000100 & self::getInt2d($recordData, 28)) >> 8;
4460
4461
            // offset within record data
4462
            $offset = 32;
4463
4464
            if ($hasDesc) {
4465
                // offset: 32; size: var; character count of description text
4466
                $dl = self::getInt4d($recordData, 32);
4467
                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
4468
                $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...
4469
                $offset += 4 + 2 * $dl;
4470
            }
4471
            if ($hasFrame) {
4472
                $fl = self::getInt4d($recordData, $offset);
4473
                $offset += 4 + 2 * $fl;
4474
            }
4475
4476
            // detect type of hyperlink (there are 4 types)
4477
            $hyperlinkType = null;
4478
4479
            if ($isUNC) {
4480
                $hyperlinkType = 'UNC';
4481
            } elseif (!$isFileLinkOrUrl) {
4482
                $hyperlinkType = 'workbook';
4483
            } elseif (ord($recordData{$offset}) == 0x03) {
4484
                $hyperlinkType = 'local';
4485
            } elseif (ord($recordData{$offset}) == 0xE0) {
4486
                $hyperlinkType = 'URL';
4487
            }
4488
4489
            switch ($hyperlinkType) {
4490
                case 'URL':
4491
                    // section 5.58.2: Hyperlink containing a URL
4492
                    // e.g. http://example.org/index.php
4493
4494
                    // offset: var; size: 16; GUID of URL Moniker
4495
                    $offset += 16;
4496
                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
4497
                    $us = self::getInt4d($recordData, $offset);
4498
                    $offset += 4;
4499
                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
4500
                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
4501
                    $nullOffset = strpos($url, 0x00);
4502
                    if ($nullOffset) {
4503
                        $url = substr($url, 0, $nullOffset);
4504
                    }
4505
                    $url .= $hasText ? '#' : '';
4506
                    $offset += $us;
4507
                    break;
4508
                case 'local':
4509
                    // section 5.58.3: Hyperlink to local file
4510
                    // examples:
4511
                    //   mydoc.txt
4512
                    //   ../../somedoc.xls#Sheet!A1
4513
4514
                    // offset: var; size: 16; GUI of File Moniker
4515
                    $offset += 16;
4516
4517
                    // offset: var; size: 2; directory up-level count.
4518
                    $upLevelCount = self::getInt2d($recordData, $offset);
4519
                    $offset += 2;
4520
4521
                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
4522
                    $sl = self::getInt4d($recordData, $offset);
4523
                    $offset += 4;
4524
4525
                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
4526
                    $shortenedFilePath = substr($recordData, $offset, $sl);
4527
                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
4528
                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
4529
4530
                    $offset += $sl;
4531
4532
                    // offset: var; size: 24; unknown sequence
4533
                    $offset += 24;
4534
4535
                    // extended file path
4536
                    // offset: var; size: 4; size of the following file link field including string lenth mark
4537
                    $sz = self::getInt4d($recordData, $offset);
4538
                    $offset += 4;
4539
4540
                    // only present if $sz > 0
4541
                    if ($sz > 0) {
4542
                        // offset: var; size: 4; size of the character array of the extended file path and name
4543
                        $xl = self::getInt4d($recordData, $offset);
4544
                        $offset += 4;
4545
4546
                        // offset: var; size 2; unknown
4547
                        $offset += 2;
4548
4549
                        // offset: var; size $xl; character array of the extended file path and name.
4550
                        $extendedFilePath = substr($recordData, $offset, $xl);
4551
                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
4552
                        $offset += $xl;
4553
                    }
4554
4555
                    // construct the path
4556
                    $url = str_repeat('..\\', $upLevelCount);
4557
                    $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...
4558
                    $url .= $hasText ? '#' : '';
4559
4560
                    break;
4561
                case 'UNC':
4562
                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
4563
                    // todo: implement
4564
                    return;
4565
                case 'workbook':
4566
                    // section 5.58.5: Hyperlink to the Current Workbook
4567
                    // e.g. Sheet2!B1:C2, stored in text mark field
4568
                    $url = 'sheet://';
4569
                    break;
4570
                default:
4571
                    return;
4572
            }
4573
4574
            if ($hasText) {
4575
                // offset: var; size: 4; character count of text mark including trailing zero word
4576
                $tl = self::getInt4d($recordData, $offset);
4577
                $offset += 4;
4578
                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
4579
                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
4580
                $url .= $text;
4581
            }
4582
4583
            // apply the hyperlink to all the relevant cells
4584
            foreach (\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
4585
                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
4586
            }
4587
        }
4588
    }
4589
4590
    /**
4591
     * Read DATAVALIDATIONS record
4592
     */
4593
    private function readDataValidations()
4594
    {
4595
        $length = self::getInt2d($this->data, $this->pos + 2);
4596
        $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...
4597
4598
        // move stream pointer forward to next record
4599
        $this->pos += 4 + $length;
4600
    }
4601
4602
    /**
4603
     * Read DATAVALIDATION record
4604
     */
4605
    private function readDataValidation()
4606
    {
4607
        $length = self::getInt2d($this->data, $this->pos + 2);
4608
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4609
4610
        // move stream pointer forward to next record
4611
        $this->pos += 4 + $length;
4612
4613
        if ($this->readDataOnly) {
4614
            return;
4615
        }
4616
4617
        // offset: 0; size: 4; Options
4618
        $options = self::getInt4d($recordData, 0);
4619
4620
        // 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...
4621
        $type = (0x0000000F & $options) >> 0;
4622 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...
4623
            case 0x00:
4624
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_NONE;
4625
                break;
4626
            case 0x01:
4627
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE;
4628
                break;
4629
            case 0x02:
4630
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_DECIMAL;
4631
                break;
4632
            case 0x03:
4633
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_LIST;
4634
                break;
4635
            case 0x04:
4636
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_DATE;
4637
                break;
4638
            case 0x05:
4639
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_TIME;
4640
                break;
4641
            case 0x06:
4642
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_TEXTLENGTH;
4643
                break;
4644
            case 0x07:
4645
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_CUSTOM;
4646
                break;
4647
        }
4648
4649
        // bit: 4-6; mask: 0x00000070; error type
4650
        $errorStyle = (0x00000070 & $options) >> 4;
4651 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...
4652
            case 0x00:
4653
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_STOP;
4654
                break;
4655
            case 0x01:
4656
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_WARNING;
4657
                break;
4658
            case 0x02:
4659
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION;
4660
                break;
4661
        }
4662
4663
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
4664
        // I have only seen cases where this is 1
4665
        $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...
4666
4667
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
4668
        $allowBlank = (0x00000100 & $options) >> 8;
4669
4670
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
4671
        $suppressDropDown = (0x00000200 & $options) >> 9;
4672
4673
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
4674
        $showInputMessage = (0x00040000 & $options) >> 18;
4675
4676
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
4677
        $showErrorMessage = (0x00080000 & $options) >> 19;
4678
4679
        // bit: 20-23; mask: 0x00F00000; condition operator
4680
        $operator = (0x00F00000 & $options) >> 20;
4681 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...
4682
            case 0x00:
4683
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_BETWEEN;
4684
                break;
4685
            case 0x01:
4686
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTBETWEEN;
4687
                break;
4688
            case 0x02:
4689
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_EQUAL;
4690
                break;
4691
            case 0x03:
4692
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTEQUAL;
4693
                break;
4694
            case 0x04:
4695
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHAN;
4696
                break;
4697
            case 0x05:
4698
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHAN;
4699
                break;
4700
            case 0x06:
4701
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHANOREQUAL;
4702
                break;
4703
            case 0x07:
4704
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHANOREQUAL;
4705
                break;
4706
        }
4707
4708
        // offset: 4; size: var; title of the prompt box
4709
        $offset = 4;
4710
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4711
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4712
        $offset += $string['size'];
4713
4714
        // offset: var; size: var; title of the error box
4715
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4716
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4717
        $offset += $string['size'];
4718
4719
        // offset: var; size: var; text of the prompt box
4720
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4721
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
4722
        $offset += $string['size'];
4723
4724
        // offset: var; size: var; text of the error box
4725
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4726
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
4727
        $offset += $string['size'];
4728
4729
        // offset: var; size: 2; size of the formula data for the first condition
4730
        $sz1 = self::getInt2d($recordData, $offset);
4731
        $offset += 2;
4732
4733
        // offset: var; size: 2; not used
4734
        $offset += 2;
4735
4736
        // offset: var; size: $sz1; formula data for first condition (without size field)
4737
        $formula1 = substr($recordData, $offset, $sz1);
4738
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
4739
        try {
4740
            $formula1 = $this->getFormulaFromStructure($formula1);
4741
4742
            // in list type validity, null characters are used as item separators
4743
            if ($type == \PhpSpreadsheet\Cell\DataValidation::TYPE_LIST) {
4744
                $formula1 = str_replace(chr(0), ',', $formula1);
4745
            }
4746
        } catch (\PhpSpreadsheet\Exception $e) {
4747
            return;
4748
        }
4749
        $offset += $sz1;
4750
4751
        // offset: var; size: 2; size of the formula data for the first condition
4752
        $sz2 = self::getInt2d($recordData, $offset);
4753
        $offset += 2;
4754
4755
        // offset: var; size: 2; not used
4756
        $offset += 2;
4757
4758
        // offset: var; size: $sz2; formula data for second condition (without size field)
4759
        $formula2 = substr($recordData, $offset, $sz2);
4760
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
4761
        try {
4762
            $formula2 = $this->getFormulaFromStructure($formula2);
4763
        } catch (\PhpSpreadsheet\Exception $e) {
4764
            return;
4765
        }
4766
        $offset += $sz2;
4767
4768
        // offset: var; size: var; cell range address list with
4769
        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
4770
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
4771
4772
        foreach ($cellRangeAddresses as $cellRange) {
4773
            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4774
            foreach (\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($stRange) as $coordinate) {
4775
                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4776
                $objValidation->setType($type);
4777
                $objValidation->setErrorStyle($errorStyle);
4778
                $objValidation->setAllowBlank((bool) $allowBlank);
4779
                $objValidation->setShowInputMessage((bool) $showInputMessage);
4780
                $objValidation->setShowErrorMessage((bool) $showErrorMessage);
4781
                $objValidation->setShowDropDown(!$suppressDropDown);
4782
                $objValidation->setOperator($operator);
4783
                $objValidation->setErrorTitle($errorTitle);
4784
                $objValidation->setError($error);
4785
                $objValidation->setPromptTitle($promptTitle);
4786
                $objValidation->setPrompt($prompt);
4787
                $objValidation->setFormula1($formula1);
4788
                $objValidation->setFormula2($formula2);
4789
            }
4790
        }
4791
    }
4792
4793
    /**
4794
     * Read SHEETLAYOUT record. Stores sheet tab color information.
4795
     */
4796
    private function readSheetLayout()
4797
    {
4798
        $length = self::getInt2d($this->data, $this->pos + 2);
4799
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4800
4801
        // move stream pointer to next record
4802
        $this->pos += 4 + $length;
4803
4804
        // local pointer in record data
4805
        $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...
4806
4807
        if (!$this->readDataOnly) {
4808
            // offset: 0; size: 2; repeated record identifier 0x0862
4809
4810
            // offset: 2; size: 10; not used
4811
4812
            // offset: 12; size: 4; size of record data
4813
            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
4814
            $sz = self::getInt4d($recordData, 12);
4815
4816
            switch ($sz) {
4817
                case 0x14:
4818
                    // offset: 16; size: 2; color index for sheet tab
4819
                    $colorIndex = self::getInt2d($recordData, 16);
4820
                    $color = Excel5\Color::map($colorIndex, $this->palette, $this->version);
4821
                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
4822
                    break;
4823
                case 0x28:
4824
                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
4825
                    return;
4826
                    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...
4827
            }
4828
        }
4829
    }
4830
4831
    /**
4832
     * Read SHEETPROTECTION record (FEATHEADR)
4833
     */
4834
    private function readSheetProtection()
4835
    {
4836
        $length = self::getInt2d($this->data, $this->pos + 2);
4837
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4838
4839
        // move stream pointer to next record
4840
        $this->pos += 4 + $length;
4841
4842
        if ($this->readDataOnly) {
4843
            return;
4844
        }
4845
4846
        // offset: 0; size: 2; repeated record header
4847
4848
        // offset: 2; size: 2; FRT cell reference flag (=0 currently)
4849
4850
        // offset: 4; size: 8; Currently not used and set to 0
4851
4852
        // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
4853
        $isf = self::getInt2d($recordData, 12);
4854
        if ($isf != 2) {
4855
            return;
4856
        }
4857
4858
        // offset: 14; size: 1; =1 since this is a feat header
4859
4860
        // offset: 15; size: 4; size of rgbHdrSData
4861
4862
        // rgbHdrSData, assume "Enhanced Protection"
4863
        // offset: 19; size: 2; option flags
4864
        $options = self::getInt2d($recordData, 19);
4865
4866
        // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
4867
        $bool = (0x0001 & $options) >> 0;
4868
        $this->phpSheet->getProtection()->setObjects(!$bool);
4869
4870
        // bit: 1; mask 0x0002; edit scenarios
4871
        $bool = (0x0002 & $options) >> 1;
4872
        $this->phpSheet->getProtection()->setScenarios(!$bool);
4873
4874
        // bit: 2; mask 0x0004; format cells
4875
        $bool = (0x0004 & $options) >> 2;
4876
        $this->phpSheet->getProtection()->setFormatCells(!$bool);
4877
4878
        // bit: 3; mask 0x0008; format columns
4879
        $bool = (0x0008 & $options) >> 3;
4880
        $this->phpSheet->getProtection()->setFormatColumns(!$bool);
4881
4882
        // bit: 4; mask 0x0010; format rows
4883
        $bool = (0x0010 & $options) >> 4;
4884
        $this->phpSheet->getProtection()->setFormatRows(!$bool);
4885
4886
        // bit: 5; mask 0x0020; insert columns
4887
        $bool = (0x0020 & $options) >> 5;
4888
        $this->phpSheet->getProtection()->setInsertColumns(!$bool);
4889
4890
        // bit: 6; mask 0x0040; insert rows
4891
        $bool = (0x0040 & $options) >> 6;
4892
        $this->phpSheet->getProtection()->setInsertRows(!$bool);
4893
4894
        // bit: 7; mask 0x0080; insert hyperlinks
4895
        $bool = (0x0080 & $options) >> 7;
4896
        $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
4897
4898
        // bit: 8; mask 0x0100; delete columns
4899
        $bool = (0x0100 & $options) >> 8;
4900
        $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
4901
4902
        // bit: 9; mask 0x0200; delete rows
4903
        $bool = (0x0200 & $options) >> 9;
4904
        $this->phpSheet->getProtection()->setDeleteRows(!$bool);
4905
4906
        // bit: 10; mask 0x0400; select locked cells
4907
        $bool = (0x0400 & $options) >> 10;
4908
        $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
4909
4910
        // bit: 11; mask 0x0800; sort cell range
4911
        $bool = (0x0800 & $options) >> 11;
4912
        $this->phpSheet->getProtection()->setSort(!$bool);
4913
4914
        // bit: 12; mask 0x1000; auto filter
4915
        $bool = (0x1000 & $options) >> 12;
4916
        $this->phpSheet->getProtection()->setAutoFilter(!$bool);
4917
4918
        // bit: 13; mask 0x2000; pivot tables
4919
        $bool = (0x2000 & $options) >> 13;
4920
        $this->phpSheet->getProtection()->setPivotTables(!$bool);
4921
4922
        // bit: 14; mask 0x4000; select unlocked cells
4923
        $bool = (0x4000 & $options) >> 14;
4924
        $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
4925
4926
        // offset: 21; size: 2; not used
4927
    }
4928
4929
    /**
4930
     * Read RANGEPROTECTION record
4931
     * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
4932
     * where it is referred to as FEAT record
4933
     */
4934
    private function readRangeProtection()
4935
    {
4936
        $length = self::getInt2d($this->data, $this->pos + 2);
4937
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4938
4939
        // move stream pointer to next record
4940
        $this->pos += 4 + $length;
4941
4942
        // local pointer in record data
4943
        $offset = 0;
4944
4945
        if (!$this->readDataOnly) {
4946
            $offset += 12;
4947
4948
            // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
4949
            $isf = self::getInt2d($recordData, 12);
4950
            if ($isf != 2) {
4951
                // we only read FEAT records of type 2
4952
                return;
4953
            }
4954
            $offset += 2;
4955
4956
            $offset += 5;
4957
4958
            // offset: 19; size: 2; count of ref ranges this feature is on
4959
            $cref = self::getInt2d($recordData, 19);
4960
            $offset += 2;
4961
4962
            $offset += 6;
4963
4964
            // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
4965
            $cellRanges = [];
4966
            for ($i = 0; $i < $cref; ++$i) {
4967
                try {
4968
                    $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
4969
                } catch (\PhpSpreadsheet\Exception $e) {
4970
                    return;
4971
                }
4972
                $cellRanges[] = $cellRange;
4973
                $offset += 8;
4974
            }
4975
4976
            // offset: var; size: var; variable length of feature specific data
4977
            $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...
4978
            $offset += 4;
4979
4980
            // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
4981
            $wPassword = self::getInt4d($recordData, $offset);
4982
            $offset += 4;
4983
4984
            // Apply range protection to sheet
4985
            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...
4986
                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
4987
            }
4988
        }
4989
    }
4990
4991
    /**
4992
     * Read IMDATA record
4993
     */
4994
    private function readImData()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
4995
    {
4996
        $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...
4997
4998
        // get spliced record data
4999
        $splicedRecordData = $this->getSplicedRecordData();
5000
        $recordData = $splicedRecordData['recordData'];
5001
5002
        // UNDER CONSTRUCTION
5003
5004
        // offset: 0; size: 2; image format
5005
        $cf = self::getInt2d($recordData, 0);
5006
5007
        // offset: 2; size: 2; environment from which the file was written
5008
        $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...
5009
5010
        // offset: 4; size: 4; length of the image data
5011
        $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...
5012
5013
        // offset: 8; size: var; image data
5014
        $iData = substr($recordData, 8);
5015
5016
        switch ($cf) {
5017
            case 0x09: // Windows bitmap format
5018
                // BITMAPCOREINFO
5019
                // 1. BITMAPCOREHEADER
5020
                // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
5021
                $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...
5022
5023
                // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
5024
                $bcWidth = self::getInt2d($iData, 4);
5025
5026
                // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
5027
                $bcHeight = self::getInt2d($iData, 6);
5028
                $ih = imagecreatetruecolor($bcWidth, $bcHeight);
5029
5030
                // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
5031
5032
                // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
5033
                $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...
5034
5035
                $rgbString = substr($iData, 12);
5036
                $rgbTriples = [];
5037
                while (strlen($rgbString) > 0) {
5038
                    $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
5039
                    $rgbString = substr($rgbString, 3);
5040
                }
5041
                $x = 0;
5042
                $y = 0;
5043
                foreach ($rgbTriples as $i => $rgbTriple) {
5044
                    $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
5045
                    imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
5046
                    $x = ($x + 1) % $bcWidth;
5047
                    $y = $y + floor(($x + 1) / $bcWidth);
5048
                }
5049
                //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...
5050
5051
                $drawing = new \PhpSpreadsheet\Worksheet\Drawing();
5052
                $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...
5053
                $drawing->setWorksheet($this->phpSheet);
5054
                break;
5055
            case 0x02: // Windows metafile or Macintosh PICT format
5056
            case 0x0e: // native format
5057
            default:
5058
                break;
5059
        }
5060
5061
        // getSplicedRecordData() takes care of moving current position in data stream
5062
    }
5063
5064
    /**
5065
     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
5066
     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
5067
     * In this case, we must treat the CONTINUE record as a MSODRAWING record
5068
     */
5069
    private function readContinue()
5070
    {
5071
        $length = self::getInt2d($this->data, $this->pos + 2);
5072
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5073
5074
        // check if we are reading drawing data
5075
        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
5076
        if ($this->drawingData == '') {
5077
            // move stream pointer to next record
5078
            $this->pos += 4 + $length;
5079
5080
            return;
5081
        }
5082
5083
        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
5084
        if ($length < 4) {
5085
            // move stream pointer to next record
5086
            $this->pos += 4 + $length;
5087
5088
            return;
5089
        }
5090
5091
        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
5092
        // look inside CONTINUE record to see if it looks like a part of an Escher stream
5093
        // we know that Escher stream may be split at least at
5094
        //        0xF003 MsofbtSpgrContainer
5095
        //        0xF004 MsofbtSpContainer
5096
        //        0xF00D MsofbtClientTextbox
5097
        $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
5098
5099
        $splitPoint = self::getInt2d($recordData, 2);
5100
        if (in_array($splitPoint, $validSplitPoints)) {
5101
            // get spliced record data (and move pointer to next record)
5102
            $splicedRecordData = $this->getSplicedRecordData();
5103
            $this->drawingData .= $splicedRecordData['recordData'];
5104
5105
            return;
5106
        }
5107
5108
        // move stream pointer to next record
5109
        $this->pos += 4 + $length;
5110
    }
5111
5112
    /**
5113
     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
5114
     * records are found. Splices the record data pieces and returns the combined string as if record data
5115
     * is in one piece.
5116
     * Moves to next current position in data stream to start of next record different from a CONtINUE record
5117
     *
5118
     * @return array
5119
     */
5120
    private function getSplicedRecordData()
5121
    {
5122
        $data = '';
5123
        $spliceOffsets = [];
5124
5125
        $i = 0;
5126
        $spliceOffsets[0] = 0;
5127
5128
        do {
5129
            ++$i;
5130
5131
            // offset: 0; size: 2; identifier
5132
            $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...
5133
            // offset: 2; size: 2; length
5134
            $length = self::getInt2d($this->data, $this->pos + 2);
5135
            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
5136
5137
            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
5138
5139
            $this->pos += 4 + $length;
5140
            $nextIdentifier = self::getInt2d($this->data, $this->pos);
5141
        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5142
5143
        $splicedData = [
5144
            'recordData' => $data,
5145
            'spliceOffsets' => $spliceOffsets,
5146
        ];
5147
5148
        return $splicedData;
5149
    }
5150
5151
    /**
5152
     * Convert formula structure into human readable Excel formula like 'A3+A5*5'
5153
     *
5154
     * @param string $formulaStructure The complete binary data for the formula
5155
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5156
     * @return string Human readable formula
5157
     */
5158
    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
5159
    {
5160
        // offset: 0; size: 2; size of the following formula data
5161
        $sz = self::getInt2d($formulaStructure, 0);
5162
5163
        // offset: 2; size: sz
5164
        $formulaData = substr($formulaStructure, 2, $sz);
5165
5166
        // offset: 2 + sz; size: variable (optional)
5167
        if (strlen($formulaStructure) > 2 + $sz) {
5168
            $additionalData = substr($formulaStructure, 2 + $sz);
5169
        } else {
5170
            $additionalData = '';
5171
        }
5172
5173
        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
5174
    }
5175
5176
    /**
5177
     * Take formula data and additional data for formula and return human readable formula
5178
     *
5179
     * @param string $formulaData The binary data for the formula itself
5180
     * @param string $additionalData Additional binary data going with the formula
5181
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5182
     * @return string Human readable formula
5183
     */
5184
    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
5185
    {
5186
        // start parsing the formula data
5187
        $tokens = [];
5188
5189
        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...
5190
            $tokens[] = $token;
5191
            $formulaData = substr($formulaData, $token['size']);
5192
        }
5193
5194
        $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
5195
5196
        return $formulaString;
5197
    }
5198
5199
    /**
5200
     * Take array of tokens together with additional data for formula and return human readable formula
5201
     *
5202
     * @param array $tokens
5203
     * @param string $additionalData Additional binary data going with the formula
5204
     * @return string Human readable formula
5205
     */
5206
    private function createFormulaFromTokens($tokens, $additionalData)
5207
    {
5208
        // empty formula?
5209
        if (empty($tokens)) {
5210
            return '';
5211
        }
5212
5213
        $formulaStrings = [];
5214
        foreach ($tokens as $token) {
5215
            // initialize spaces
5216
            $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
5217
            $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
5218
            $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
5219
            $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
5220
            $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
5221
            $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
5222
5223
            switch ($token['name']) {
5224
                case 'tAdd': // addition
5225
                case 'tConcat': // addition
5226
                case 'tDiv': // division
5227
                case 'tEQ': // equality
5228
                case 'tGE': // greater than or equal
5229
                case 'tGT': // greater than
5230
                case 'tIsect': // intersection
5231
                case 'tLE': // less than or equal
5232
                case 'tList': // less than or equal
5233
                case 'tLT': // less than
5234
                case 'tMul': // multiplication
5235
                case 'tNE': // multiplication
5236
                case 'tPower': // power
5237
                case 'tRange': // range
5238
                case 'tSub': // subtraction
5239
                    $op2 = array_pop($formulaStrings);
5240
                    $op1 = array_pop($formulaStrings);
5241
                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
5242
                    unset($space0, $space1);
5243
                    break;
5244
                case 'tUplus': // unary plus
5245 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...
5246
                    $op = array_pop($formulaStrings);
5247
                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
5248
                    unset($space0, $space1);
5249
                    break;
5250 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...
5251
                    $op = array_pop($formulaStrings);
5252
                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
5253
                    unset($space0, $space1);
5254
                    break;
5255
                case 'tAttrVolatile': // indicates volatile function
5256
                case 'tAttrIf':
5257
                case 'tAttrSkip':
5258
                case 'tAttrChoose':
5259
                    // token is only important for Excel formula evaluator
5260
                    // do nothing
5261
                    break;
5262
                case 'tAttrSpace': // space / carriage return
5263
                    // space will be used when next token arrives, do not alter formulaString stack
5264
                    switch ($token['data']['spacetype']) {
5265
                        case 'type0':
5266
                            $space0 = str_repeat(' ', $token['data']['spacecount']);
5267
                            break;
5268
                        case 'type1':
5269
                            $space1 = str_repeat("\n", $token['data']['spacecount']);
5270
                            break;
5271
                        case 'type2':
5272
                            $space2 = str_repeat(' ', $token['data']['spacecount']);
5273
                            break;
5274
                        case 'type3':
5275
                            $space3 = str_repeat("\n", $token['data']['spacecount']);
5276
                            break;
5277
                        case 'type4':
5278
                            $space4 = str_repeat(' ', $token['data']['spacecount']);
5279
                            break;
5280
                        case 'type5':
5281
                            $space5 = str_repeat("\n", $token['data']['spacecount']);
5282
                            break;
5283
                    }
5284
                    break;
5285
                case 'tAttrSum': // SUM function with one parameter
5286
                    $op = array_pop($formulaStrings);
5287
                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
5288
                    unset($space0, $space1);
5289
                    break;
5290
                case 'tFunc': // function with fixed number of arguments
5291
                case 'tFuncV': // function with variable number of arguments
5292
                    if ($token['data']['function'] != '') {
5293
                        // normal function
5294
                        $ops = []; // array of operators
5295 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...
5296
                            $ops[] = array_pop($formulaStrings);
5297
                        }
5298
                        $ops = array_reverse($ops);
5299
                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
5300
                        unset($space0, $space1);
5301
                    } else {
5302
                        // add-in function
5303
                        $ops = []; // array of operators
5304 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...
5305
                            $ops[] = array_pop($formulaStrings);
5306
                        }
5307
                        $ops = array_reverse($ops);
5308
                        $function = array_pop($formulaStrings);
5309
                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
5310
                        unset($space0, $space1);
5311
                    }
5312
                    break;
5313
                case 'tParen': // parenthesis
5314
                    $expression = array_pop($formulaStrings);
5315
                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
5316
                    unset($space2, $space3, $space4, $space5);
5317
                    break;
5318
                case 'tArray': // array constant
5319
                    $constantArray = self::readBIFF8ConstantArray($additionalData);
5320
                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
5321
                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
5322
                    unset($space0, $space1);
5323
                    break;
5324
                case 'tMemArea':
5325
                    // bite off chunk of additional data
5326
                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
5327
                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
5328
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5329
                    unset($space0, $space1);
5330
                    break;
5331
                case 'tArea': // cell range address
5332
                case 'tBool': // boolean
5333
                case 'tErr': // error code
5334
                case 'tInt': // integer
5335
                case 'tMemErr':
5336
                case 'tMemFunc':
5337
                case 'tMissArg':
5338
                case 'tName':
5339
                case 'tNameX':
5340
                case 'tNum': // number
5341
                case 'tRef': // single cell reference
5342
                case 'tRef3d': // 3d cell reference
5343
                case 'tArea3d': // 3d cell range reference
5344
                case 'tRefN':
5345
                case 'tAreaN':
5346
                case 'tStr': // string
5347
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5348
                    unset($space0, $space1);
5349
                    break;
5350
            }
5351
        }
5352
        $formulaString = $formulaStrings[0];
5353
5354
        return $formulaString;
5355
    }
5356
5357
    /**
5358
     * Fetch next token from binary formula data
5359
     *
5360
     * @param string $formulaData Formula data
5361
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5362
     * @throws Exception
5363
     * @return array
5364
     */
5365
    private function getNextToken($formulaData, $baseCell = 'A1')
5366
    {
5367
        // offset: 0; size: 1; token id
5368
        $id = ord($formulaData[0]); // token id
5369
        $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...
5370
5371
        switch ($id) {
5372
            case 0x03:
5373
                $name = 'tAdd';
5374
                $size = 1;
5375
                $data = '+';
5376
                break;
5377
            case 0x04:
5378
                $name = 'tSub';
5379
                $size = 1;
5380
                $data = '-';
5381
                break;
5382
            case 0x05:
5383
                $name = 'tMul';
5384
                $size = 1;
5385
                $data = '*';
5386
                break;
5387
            case 0x06:
5388
                $name = 'tDiv';
5389
                $size = 1;
5390
                $data = '/';
5391
                break;
5392
            case 0x07:
5393
                $name = 'tPower';
5394
                $size = 1;
5395
                $data = '^';
5396
                break;
5397
            case 0x08:
5398
                $name = 'tConcat';
5399
                $size = 1;
5400
                $data = '&';
5401
                break;
5402
            case 0x09:
5403
                $name = 'tLT';
5404
                $size = 1;
5405
                $data = '<';
5406
                break;
5407
            case 0x0A:
5408
                $name = 'tLE';
5409
                $size = 1;
5410
                $data = '<=';
5411
                break;
5412
            case 0x0B:
5413
                $name = 'tEQ';
5414
                $size = 1;
5415
                $data = '=';
5416
                break;
5417
            case 0x0C:
5418
                $name = 'tGE';
5419
                $size = 1;
5420
                $data = '>=';
5421
                break;
5422
            case 0x0D:
5423
                $name = 'tGT';
5424
                $size = 1;
5425
                $data = '>';
5426
                break;
5427
            case 0x0E:
5428
                $name = 'tNE';
5429
                $size = 1;
5430
                $data = '<>';
5431
                break;
5432
            case 0x0F:
5433
                $name = 'tIsect';
5434
                $size = 1;
5435
                $data = ' ';
5436
                break;
5437
            case 0x10:
5438
                $name = 'tList';
5439
                $size = 1;
5440
                $data = ',';
5441
                break;
5442
            case 0x11:
5443
                $name = 'tRange';
5444
                $size = 1;
5445
                $data = ':';
5446
                break;
5447
            case 0x12:
5448
                $name = 'tUplus';
5449
                $size = 1;
5450
                $data = '+';
5451
                break;
5452
            case 0x13:
5453
                $name = 'tUminus';
5454
                $size = 1;
5455
                $data = '-';
5456
                break;
5457
            case 0x14:
5458
                $name = 'tPercent';
5459
                $size = 1;
5460
                $data = '%';
5461
                break;
5462
            case 0x15:    //    parenthesis
5463
                $name = 'tParen';
5464
                $size = 1;
5465
                $data = null;
5466
                break;
5467
            case 0x16:    //    missing argument
5468
                $name = 'tMissArg';
5469
                $size = 1;
5470
                $data = '';
5471
                break;
5472
            case 0x17:    //    string
5473
                $name = 'tStr';
5474
                // offset: 1; size: var; Unicode string, 8-bit string length
5475
                $string = self::readUnicodeStringShort(substr($formulaData, 1));
5476
                $size = 1 + $string['size'];
5477
                $data = self::UTF8toExcelDoubleQuoted($string['value']);
5478
                break;
5479
            case 0x19:    //    Special attribute
5480
                // offset: 1; size: 1; attribute type flags:
5481
                switch (ord($formulaData[1])) {
5482
                    case 0x01:
5483
                        $name = 'tAttrVolatile';
5484
                        $size = 4;
5485
                        $data = null;
5486
                        break;
5487
                    case 0x02:
5488
                        $name = 'tAttrIf';
5489
                        $size = 4;
5490
                        $data = null;
5491
                        break;
5492
                    case 0x04:
5493
                        $name = 'tAttrChoose';
5494
                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
5495
                        $nc = self::getInt2d($formulaData, 2);
5496
                        // offset: 4; size: 2 * $nc
5497
                        // offset: 4 + 2 * $nc; size: 2
5498
                        $size = 2 * $nc + 6;
5499
                        $data = null;
5500
                        break;
5501
                    case 0x08:
5502
                        $name = 'tAttrSkip';
5503
                        $size = 4;
5504
                        $data = null;
5505
                        break;
5506
                    case 0x10:
5507
                        $name = 'tAttrSum';
5508
                        $size = 4;
5509
                        $data = null;
5510
                        break;
5511
                    case 0x40:
5512
                    case 0x41:
5513
                        $name = 'tAttrSpace';
5514
                        $size = 4;
5515
                        // offset: 2; size: 2; space type and position
5516
                        switch (ord($formulaData[2])) {
5517
                            case 0x00:
5518
                                $spacetype = 'type0';
5519
                                break;
5520
                            case 0x01:
5521
                                $spacetype = 'type1';
5522
                                break;
5523
                            case 0x02:
5524
                                $spacetype = 'type2';
5525
                                break;
5526
                            case 0x03:
5527
                                $spacetype = 'type3';
5528
                                break;
5529
                            case 0x04:
5530
                                $spacetype = 'type4';
5531
                                break;
5532
                            case 0x05:
5533
                                $spacetype = 'type5';
5534
                                break;
5535
                            default:
5536
                                throw new Exception('Unrecognized space type in tAttrSpace token');
5537
                                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...
5538
                        }
5539
                        // offset: 3; size: 1; number of inserted spaces/carriage returns
5540
                        $spacecount = ord($formulaData[3]);
5541
5542
                        $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
5543
                        break;
5544
                    default:
5545
                        throw new Exception('Unrecognized attribute flag in tAttr token');
5546
                        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...
5547
                }
5548
                break;
5549
            case 0x1C:    //    error code
5550
                // offset: 1; size: 1; error code
5551
                $name = 'tErr';
5552
                $size = 2;
5553
                $data = Excel5\ErrorCode::lookup(ord($formulaData[1]));
5554
                break;
5555
            case 0x1D:    //    boolean
5556
                // 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...
5557
                $name = 'tBool';
5558
                $size = 2;
5559
                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
5560
                break;
5561
            case 0x1E:    //    integer
5562
                // offset: 1; size: 2; unsigned 16-bit integer
5563
                $name = 'tInt';
5564
                $size = 3;
5565
                $data = self::getInt2d($formulaData, 1);
5566
                break;
5567
            case 0x1F:    //    number
5568
                // 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...
5569
                $name = 'tNum';
5570
                $size = 9;
5571
                $data = self::extractNumber(substr($formulaData, 1));
5572
                $data = str_replace(',', '.', (string) $data); // in case non-English locale
5573
                break;
5574
            case 0x20:    //    array constant
5575
            case 0x40:
5576
            case 0x60:
5577
                // offset: 1; size: 7; not used
5578
                $name = 'tArray';
5579
                $size = 8;
5580
                $data = null;
5581
                break;
5582
            case 0x21:    //    function with fixed number of arguments
5583
            case 0x41:
5584
            case 0x61:
5585
                $name = 'tFunc';
5586
                $size = 3;
5587
                // offset: 1; size: 2; index to built-in sheet function
5588
                switch (self::getInt2d($formulaData, 1)) {
5589
                    case 2:
5590
                        $function = 'ISNA';
5591
                        $args = 1;
5592
                        break;
5593
                    case 3:
5594
                        $function = 'ISERROR';
5595
                        $args = 1;
5596
                        break;
5597
                    case 10:
5598
                        $function = 'NA';
5599
                        $args = 0;
5600
                        break;
5601
                    case 15:
5602
                        $function = 'SIN';
5603
                        $args = 1;
5604
                        break;
5605
                    case 16:
5606
                        $function = 'COS';
5607
                        $args = 1;
5608
                        break;
5609
                    case 17:
5610
                        $function = 'TAN';
5611
                        $args = 1;
5612
                        break;
5613
                    case 18:
5614
                        $function = 'ATAN';
5615
                        $args = 1;
5616
                        break;
5617
                    case 19:
5618
                        $function = 'PI';
5619
                        $args = 0;
5620
                        break;
5621
                    case 20:
5622
                        $function = 'SQRT';
5623
                        $args = 1;
5624
                        break;
5625
                    case 21:
5626
                        $function = 'EXP';
5627
                        $args = 1;
5628
                        break;
5629
                    case 22:
5630
                        $function = 'LN';
5631
                        $args = 1;
5632
                        break;
5633
                    case 23:
5634
                        $function = 'LOG10';
5635
                        $args = 1;
5636
                        break;
5637
                    case 24:
5638
                        $function = 'ABS';
5639
                        $args = 1;
5640
                        break;
5641
                    case 25:
5642
                        $function = 'INT';
5643
                        $args = 1;
5644
                        break;
5645
                    case 26:
5646
                        $function = 'SIGN';
5647
                        $args = 1;
5648
                        break;
5649
                    case 27:
5650
                        $function = 'ROUND';
5651
                        $args = 2;
5652
                        break;
5653
                    case 30:
5654
                        $function = 'REPT';
5655
                        $args = 2;
5656
                        break;
5657
                    case 31:
5658
                        $function = 'MID';
5659
                        $args = 3;
5660
                        break;
5661
                    case 32:
5662
                        $function = 'LEN';
5663
                        $args = 1;
5664
                        break;
5665
                    case 33:
5666
                        $function = 'VALUE';
5667
                        $args = 1;
5668
                        break;
5669
                    case 34:
5670
                        $function = 'TRUE';
5671
                        $args = 0;
5672
                        break;
5673
                    case 35:
5674
                        $function = 'FALSE';
5675
                        $args = 0;
5676
                        break;
5677
                    case 38:
5678
                        $function = 'NOT';
5679
                        $args = 1;
5680
                        break;
5681
                    case 39:
5682
                        $function = 'MOD';
5683
                        $args = 2;
5684
                        break;
5685
                    case 40:
5686
                        $function = 'DCOUNT';
5687
                        $args = 3;
5688
                        break;
5689
                    case 41:
5690
                        $function = 'DSUM';
5691
                        $args = 3;
5692
                        break;
5693
                    case 42:
5694
                        $function = 'DAVERAGE';
5695
                        $args = 3;
5696
                        break;
5697
                    case 43:
5698
                        $function = 'DMIN';
5699
                        $args = 3;
5700
                        break;
5701
                    case 44:
5702
                        $function = 'DMAX';
5703
                        $args = 3;
5704
                        break;
5705
                    case 45:
5706
                        $function = 'DSTDEV';
5707
                        $args = 3;
5708
                        break;
5709
                    case 48:
5710
                        $function = 'TEXT';
5711
                        $args = 2;
5712
                        break;
5713
                    case 61:
5714
                        $function = 'MIRR';
5715
                        $args = 3;
5716
                        break;
5717
                    case 63:
5718
                        $function = 'RAND';
5719
                        $args = 0;
5720
                        break;
5721
                    case 65:
5722
                        $function = 'DATE';
5723
                        $args = 3;
5724
                        break;
5725
                    case 66:
5726
                        $function = 'TIME';
5727
                        $args = 3;
5728
                        break;
5729
                    case 67:
5730
                        $function = 'DAY';
5731
                        $args = 1;
5732
                        break;
5733
                    case 68:
5734
                        $function = 'MONTH';
5735
                        $args = 1;
5736
                        break;
5737
                    case 69:
5738
                        $function = 'YEAR';
5739
                        $args = 1;
5740
                        break;
5741
                    case 71:
5742
                        $function = 'HOUR';
5743
                        $args = 1;
5744
                        break;
5745
                    case 72:
5746
                        $function = 'MINUTE';
5747
                        $args = 1;
5748
                        break;
5749
                    case 73:
5750
                        $function = 'SECOND';
5751
                        $args = 1;
5752
                        break;
5753
                    case 74:
5754
                        $function = 'NOW';
5755
                        $args = 0;
5756
                        break;
5757
                    case 75:
5758
                        $function = 'AREAS';
5759
                        $args = 1;
5760
                        break;
5761
                    case 76:
5762
                        $function = 'ROWS';
5763
                        $args = 1;
5764
                        break;
5765
                    case 77:
5766
                        $function = 'COLUMNS';
5767
                        $args = 1;
5768
                        break;
5769
                    case 83:
5770
                        $function = 'TRANSPOSE';
5771
                        $args = 1;
5772
                        break;
5773
                    case 86:
5774
                        $function = 'TYPE';
5775
                        $args = 1;
5776
                        break;
5777
                    case 97:
5778
                        $function = 'ATAN2';
5779
                        $args = 2;
5780
                        break;
5781
                    case 98:
5782
                        $function = 'ASIN';
5783
                        $args = 1;
5784
                        break;
5785
                    case 99:
5786
                        $function = 'ACOS';
5787
                        $args = 1;
5788
                        break;
5789
                    case 105:
5790
                        $function = 'ISREF';
5791
                        $args = 1;
5792
                        break;
5793
                    case 111:
5794
                        $function = 'CHAR';
5795
                        $args = 1;
5796
                        break;
5797
                    case 112:
5798
                        $function = 'LOWER';
5799
                        $args = 1;
5800
                        break;
5801
                    case 113:
5802
                        $function = 'UPPER';
5803
                        $args = 1;
5804
                        break;
5805
                    case 114:
5806
                        $function = 'PROPER';
5807
                        $args = 1;
5808
                        break;
5809
                    case 117:
5810
                        $function = 'EXACT';
5811
                        $args = 2;
5812
                        break;
5813
                    case 118:
5814
                        $function = 'TRIM';
5815
                        $args = 1;
5816
                        break;
5817
                    case 119:
5818
                        $function = 'REPLACE';
5819
                        $args = 4;
5820
                        break;
5821
                    case 121:
5822
                        $function = 'CODE';
5823
                        $args = 1;
5824
                        break;
5825
                    case 126:
5826
                        $function = 'ISERR';
5827
                        $args = 1;
5828
                        break;
5829
                    case 127:
5830
                        $function = 'ISTEXT';
5831
                        $args = 1;
5832
                        break;
5833
                    case 128:
5834
                        $function = 'ISNUMBER';
5835
                        $args = 1;
5836
                        break;
5837
                    case 129:
5838
                        $function = 'ISBLANK';
5839
                        $args = 1;
5840
                        break;
5841
                    case 130:
5842
                        $function = 'T';
5843
                        $args = 1;
5844
                        break;
5845
                    case 131:
5846
                        $function = 'N';
5847
                        $args = 1;
5848
                        break;
5849
                    case 140:
5850
                        $function = 'DATEVALUE';
5851
                        $args = 1;
5852
                        break;
5853
                    case 141:
5854
                        $function = 'TIMEVALUE';
5855
                        $args = 1;
5856
                        break;
5857
                    case 142:
5858
                        $function = 'SLN';
5859
                        $args = 3;
5860
                        break;
5861
                    case 143:
5862
                        $function = 'SYD';
5863
                        $args = 4;
5864
                        break;
5865
                    case 162:
5866
                        $function = 'CLEAN';
5867
                        $args = 1;
5868
                        break;
5869
                    case 163:
5870
                        $function = 'MDETERM';
5871
                        $args = 1;
5872
                        break;
5873
                    case 164:
5874
                        $function = 'MINVERSE';
5875
                        $args = 1;
5876
                        break;
5877
                    case 165:
5878
                        $function = 'MMULT';
5879
                        $args = 2;
5880
                        break;
5881
                    case 184:
5882
                        $function = 'FACT';
5883
                        $args = 1;
5884
                        break;
5885
                    case 189:
5886
                        $function = 'DPRODUCT';
5887
                        $args = 3;
5888
                        break;
5889
                    case 190:
5890
                        $function = 'ISNONTEXT';
5891
                        $args = 1;
5892
                        break;
5893
                    case 195:
5894
                        $function = 'DSTDEVP';
5895
                        $args = 3;
5896
                        break;
5897
                    case 196:
5898
                        $function = 'DVARP';
5899
                        $args = 3;
5900
                        break;
5901
                    case 198:
5902
                        $function = 'ISLOGICAL';
5903
                        $args = 1;
5904
                        break;
5905
                    case 199:
5906
                        $function = 'DCOUNTA';
5907
                        $args = 3;
5908
                        break;
5909
                    case 207:
5910
                        $function = 'REPLACEB';
5911
                        $args = 4;
5912
                        break;
5913
                    case 210:
5914
                        $function = 'MIDB';
5915
                        $args = 3;
5916
                        break;
5917
                    case 211:
5918
                        $function = 'LENB';
5919
                        $args = 1;
5920
                        break;
5921
                    case 212:
5922
                        $function = 'ROUNDUP';
5923
                        $args = 2;
5924
                        break;
5925
                    case 213:
5926
                        $function = 'ROUNDDOWN';
5927
                        $args = 2;
5928
                        break;
5929
                    case 214:
5930
                        $function = 'ASC';
5931
                        $args = 1;
5932
                        break;
5933
                    case 215:
5934
                        $function = 'DBCS';
5935
                        $args = 1;
5936
                        break;
5937
                    case 221:
5938
                        $function = 'TODAY';
5939
                        $args = 0;
5940
                        break;
5941
                    case 229:
5942
                        $function = 'SINH';
5943
                        $args = 1;
5944
                        break;
5945
                    case 230:
5946
                        $function = 'COSH';
5947
                        $args = 1;
5948
                        break;
5949
                    case 231:
5950
                        $function = 'TANH';
5951
                        $args = 1;
5952
                        break;
5953
                    case 232:
5954
                        $function = 'ASINH';
5955
                        $args = 1;
5956
                        break;
5957
                    case 233:
5958
                        $function = 'ACOSH';
5959
                        $args = 1;
5960
                        break;
5961
                    case 234:
5962
                        $function = 'ATANH';
5963
                        $args = 1;
5964
                        break;
5965
                    case 235:
5966
                        $function = 'DGET';
5967
                        $args = 3;
5968
                        break;
5969
                    case 244:
5970
                        $function = 'INFO';
5971
                        $args = 1;
5972
                        break;
5973
                    case 252:
5974
                        $function = 'FREQUENCY';
5975
                        $args = 2;
5976
                        break;
5977
                    case 261:
5978
                        $function = 'ERROR.TYPE';
5979
                        $args = 1;
5980
                        break;
5981
                    case 271:
5982
                        $function = 'GAMMALN';
5983
                        $args = 1;
5984
                        break;
5985
                    case 273:
5986
                        $function = 'BINOMDIST';
5987
                        $args = 4;
5988
                        break;
5989
                    case 274:
5990
                        $function = 'CHIDIST';
5991
                        $args = 2;
5992
                        break;
5993
                    case 275:
5994
                        $function = 'CHIINV';
5995
                        $args = 2;
5996
                        break;
5997
                    case 276:
5998
                        $function = 'COMBIN';
5999
                        $args = 2;
6000
                        break;
6001
                    case 277:
6002
                        $function = 'CONFIDENCE';
6003
                        $args = 3;
6004
                        break;
6005
                    case 278:
6006
                        $function = 'CRITBINOM';
6007
                        $args = 3;
6008
                        break;
6009
                    case 279:
6010
                        $function = 'EVEN';
6011
                        $args = 1;
6012
                        break;
6013
                    case 280:
6014
                        $function = 'EXPONDIST';
6015
                        $args = 3;
6016
                        break;
6017
                    case 281:
6018
                        $function = 'FDIST';
6019
                        $args = 3;
6020
                        break;
6021
                    case 282:
6022
                        $function = 'FINV';
6023
                        $args = 3;
6024
                        break;
6025
                    case 283:
6026
                        $function = 'FISHER';
6027
                        $args = 1;
6028
                        break;
6029
                    case 284:
6030
                        $function = 'FISHERINV';
6031
                        $args = 1;
6032
                        break;
6033
                    case 285:
6034
                        $function = 'FLOOR';
6035
                        $args = 2;
6036
                        break;
6037
                    case 286:
6038
                        $function = 'GAMMADIST';
6039
                        $args = 4;
6040
                        break;
6041
                    case 287:
6042
                        $function = 'GAMMAINV';
6043
                        $args = 3;
6044
                        break;
6045
                    case 288:
6046
                        $function = 'CEILING';
6047
                        $args = 2;
6048
                        break;
6049
                    case 289:
6050
                        $function = 'HYPGEOMDIST';
6051
                        $args = 4;
6052
                        break;
6053
                    case 290:
6054
                        $function = 'LOGNORMDIST';
6055
                        $args = 3;
6056
                        break;
6057
                    case 291:
6058
                        $function = 'LOGINV';
6059
                        $args = 3;
6060
                        break;
6061
                    case 292:
6062
                        $function = 'NEGBINOMDIST';
6063
                        $args = 3;
6064
                        break;
6065
                    case 293:
6066
                        $function = 'NORMDIST';
6067
                        $args = 4;
6068
                        break;
6069
                    case 294:
6070
                        $function = 'NORMSDIST';
6071
                        $args = 1;
6072
                        break;
6073
                    case 295:
6074
                        $function = 'NORMINV';
6075
                        $args = 3;
6076
                        break;
6077
                    case 296:
6078
                        $function = 'NORMSINV';
6079
                        $args = 1;
6080
                        break;
6081
                    case 297:
6082
                        $function = 'STANDARDIZE';
6083
                        $args = 3;
6084
                        break;
6085
                    case 298:
6086
                        $function = 'ODD';
6087
                        $args = 1;
6088
                        break;
6089
                    case 299:
6090
                        $function = 'PERMUT';
6091
                        $args = 2;
6092
                        break;
6093
                    case 300:
6094
                        $function = 'POISSON';
6095
                        $args = 3;
6096
                        break;
6097
                    case 301:
6098
                        $function = 'TDIST';
6099
                        $args = 3;
6100
                        break;
6101
                    case 302:
6102
                        $function = 'WEIBULL';
6103
                        $args = 4;
6104
                        break;
6105
                    case 303:
6106
                        $function = 'SUMXMY2';
6107
                        $args = 2;
6108
                        break;
6109
                    case 304:
6110
                        $function = 'SUMX2MY2';
6111
                        $args = 2;
6112
                        break;
6113
                    case 305:
6114
                        $function = 'SUMX2PY2';
6115
                        $args = 2;
6116
                        break;
6117
                    case 306:
6118
                        $function = 'CHITEST';
6119
                        $args = 2;
6120
                        break;
6121
                    case 307:
6122
                        $function = 'CORREL';
6123
                        $args = 2;
6124
                        break;
6125
                    case 308:
6126
                        $function = 'COVAR';
6127
                        $args = 2;
6128
                        break;
6129
                    case 309:
6130
                        $function = 'FORECAST';
6131
                        $args = 3;
6132
                        break;
6133
                    case 310:
6134
                        $function = 'FTEST';
6135
                        $args = 2;
6136
                        break;
6137
                    case 311:
6138
                        $function = 'INTERCEPT';
6139
                        $args = 2;
6140
                        break;
6141
                    case 312:
6142
                        $function = 'PEARSON';
6143
                        $args = 2;
6144
                        break;
6145
                    case 313:
6146
                        $function = 'RSQ';
6147
                        $args = 2;
6148
                        break;
6149
                    case 314:
6150
                        $function = 'STEYX';
6151
                        $args = 2;
6152
                        break;
6153
                    case 315:
6154
                        $function = 'SLOPE';
6155
                        $args = 2;
6156
                        break;
6157
                    case 316:
6158
                        $function = 'TTEST';
6159
                        $args = 4;
6160
                        break;
6161
                    case 325:
6162
                        $function = 'LARGE';
6163
                        $args = 2;
6164
                        break;
6165
                    case 326:
6166
                        $function = 'SMALL';
6167
                        $args = 2;
6168
                        break;
6169
                    case 327:
6170
                        $function = 'QUARTILE';
6171
                        $args = 2;
6172
                        break;
6173
                    case 328:
6174
                        $function = 'PERCENTILE';
6175
                        $args = 2;
6176
                        break;
6177
                    case 331:
6178
                        $function = 'TRIMMEAN';
6179
                        $args = 2;
6180
                        break;
6181
                    case 332:
6182
                        $function = 'TINV';
6183
                        $args = 2;
6184
                        break;
6185
                    case 337:
6186
                        $function = 'POWER';
6187
                        $args = 2;
6188
                        break;
6189
                    case 342:
6190
                        $function = 'RADIANS';
6191
                        $args = 1;
6192
                        break;
6193
                    case 343:
6194
                        $function = 'DEGREES';
6195
                        $args = 1;
6196
                        break;
6197
                    case 346:
6198
                        $function = 'COUNTIF';
6199
                        $args = 2;
6200
                        break;
6201
                    case 347:
6202
                        $function = 'COUNTBLANK';
6203
                        $args = 1;
6204
                        break;
6205
                    case 350:
6206
                        $function = 'ISPMT';
6207
                        $args = 4;
6208
                        break;
6209
                    case 351:
6210
                        $function = 'DATEDIF';
6211
                        $args = 3;
6212
                        break;
6213
                    case 352:
6214
                        $function = 'DATESTRING';
6215
                        $args = 1;
6216
                        break;
6217
                    case 353:
6218
                        $function = 'NUMBERSTRING';
6219
                        $args = 2;
6220
                        break;
6221
                    case 360:
6222
                        $function = 'PHONETIC';
6223
                        $args = 1;
6224
                        break;
6225
                    case 368:
6226
                        $function = 'BAHTTEXT';
6227
                        $args = 1;
6228
                        break;
6229
                    default:
6230
                        throw new Exception('Unrecognized function in formula');
6231
                        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...
6232
                }
6233
                $data = ['function' => $function, 'args' => $args];
6234
                break;
6235
            case 0x22:    //    function with variable number of arguments
6236
            case 0x42:
6237
            case 0x62:
6238
                $name = 'tFuncV';
6239
                $size = 4;
6240
                // offset: 1; size: 1; number of arguments
6241
                $args = ord($formulaData[1]);
6242
                // offset: 2: size: 2; index to built-in sheet function
6243
                $index = self::getInt2d($formulaData, 2);
6244
                switch ($index) {
6245
                    case 0:
6246
                        $function = 'COUNT';
6247
                        break;
6248
                    case 1:
6249
                        $function = 'IF';
6250
                        break;
6251
                    case 4:
6252
                        $function = 'SUM';
6253
                        break;
6254
                    case 5:
6255
                        $function = 'AVERAGE';
6256
                        break;
6257
                    case 6:
6258
                        $function = 'MIN';
6259
                        break;
6260
                    case 7:
6261
                        $function = 'MAX';
6262
                        break;
6263
                    case 8:
6264
                        $function = 'ROW';
6265
                        break;
6266
                    case 9:
6267
                        $function = 'COLUMN';
6268
                        break;
6269
                    case 11:
6270
                        $function = 'NPV';
6271
                        break;
6272
                    case 12:
6273
                        $function = 'STDEV';
6274
                        break;
6275
                    case 13:
6276
                        $function = 'DOLLAR';
6277
                        break;
6278
                    case 14:
6279
                        $function = 'FIXED';
6280
                        break;
6281
                    case 28:
6282
                        $function = 'LOOKUP';
6283
                        break;
6284
                    case 29:
6285
                        $function = 'INDEX';
6286
                        break;
6287
                    case 36:
6288
                        $function = 'AND';
6289
                        break;
6290
                    case 37:
6291
                        $function = 'OR';
6292
                        break;
6293
                    case 46:
6294
                        $function = 'VAR';
6295
                        break;
6296
                    case 49:
6297
                        $function = 'LINEST';
6298
                        break;
6299
                    case 50:
6300
                        $function = 'TREND';
6301
                        break;
6302
                    case 51:
6303
                        $function = 'LOGEST';
6304
                        break;
6305
                    case 52:
6306
                        $function = 'GROWTH';
6307
                        break;
6308
                    case 56:
6309
                        $function = 'PV';
6310
                        break;
6311
                    case 57:
6312
                        $function = 'FV';
6313
                        break;
6314
                    case 58:
6315
                        $function = 'NPER';
6316
                        break;
6317
                    case 59:
6318
                        $function = 'PMT';
6319
                        break;
6320
                    case 60:
6321
                        $function = 'RATE';
6322
                        break;
6323
                    case 62:
6324
                        $function = 'IRR';
6325
                        break;
6326
                    case 64:
6327
                        $function = 'MATCH';
6328
                        break;
6329
                    case 70:
6330
                        $function = 'WEEKDAY';
6331
                        break;
6332
                    case 78:
6333
                        $function = 'OFFSET';
6334
                        break;
6335
                    case 82:
6336
                        $function = 'SEARCH';
6337
                        break;
6338
                    case 100:
6339
                        $function = 'CHOOSE';
6340
                        break;
6341
                    case 101:
6342
                        $function = 'HLOOKUP';
6343
                        break;
6344
                    case 102:
6345
                        $function = 'VLOOKUP';
6346
                        break;
6347
                    case 109:
6348
                        $function = 'LOG';
6349
                        break;
6350
                    case 115:
6351
                        $function = 'LEFT';
6352
                        break;
6353
                    case 116:
6354
                        $function = 'RIGHT';
6355
                        break;
6356
                    case 120:
6357
                        $function = 'SUBSTITUTE';
6358
                        break;
6359
                    case 124:
6360
                        $function = 'FIND';
6361
                        break;
6362
                    case 125:
6363
                        $function = 'CELL';
6364
                        break;
6365
                    case 144:
6366
                        $function = 'DDB';
6367
                        break;
6368
                    case 148:
6369
                        $function = 'INDIRECT';
6370
                        break;
6371
                    case 167:
6372
                        $function = 'IPMT';
6373
                        break;
6374
                    case 168:
6375
                        $function = 'PPMT';
6376
                        break;
6377
                    case 169:
6378
                        $function = 'COUNTA';
6379
                        break;
6380
                    case 183:
6381
                        $function = 'PRODUCT';
6382
                        break;
6383
                    case 193:
6384
                        $function = 'STDEVP';
6385
                        break;
6386
                    case 194:
6387
                        $function = 'VARP';
6388
                        break;
6389
                    case 197:
6390
                        $function = 'TRUNC';
6391
                        break;
6392
                    case 204:
6393
                        $function = 'USDOLLAR';
6394
                        break;
6395
                    case 205:
6396
                        $function = 'FINDB';
6397
                        break;
6398
                    case 206:
6399
                        $function = 'SEARCHB';
6400
                        break;
6401
                    case 208:
6402
                        $function = 'LEFTB';
6403
                        break;
6404
                    case 209:
6405
                        $function = 'RIGHTB';
6406
                        break;
6407
                    case 216:
6408
                        $function = 'RANK';
6409
                        break;
6410
                    case 219:
6411
                        $function = 'ADDRESS';
6412
                        break;
6413
                    case 220:
6414
                        $function = 'DAYS360';
6415
                        break;
6416
                    case 222:
6417
                        $function = 'VDB';
6418
                        break;
6419
                    case 227:
6420
                        $function = 'MEDIAN';
6421
                        break;
6422
                    case 228:
6423
                        $function = 'SUMPRODUCT';
6424
                        break;
6425
                    case 247:
6426
                        $function = 'DB';
6427
                        break;
6428
                    case 255:
6429
                        $function = '';
6430
                        break;
6431
                    case 269:
6432
                        $function = 'AVEDEV';
6433
                        break;
6434
                    case 270:
6435
                        $function = 'BETADIST';
6436
                        break;
6437
                    case 272:
6438
                        $function = 'BETAINV';
6439
                        break;
6440
                    case 317:
6441
                        $function = 'PROB';
6442
                        break;
6443
                    case 318:
6444
                        $function = 'DEVSQ';
6445
                        break;
6446
                    case 319:
6447
                        $function = 'GEOMEAN';
6448
                        break;
6449
                    case 320:
6450
                        $function = 'HARMEAN';
6451
                        break;
6452
                    case 321:
6453
                        $function = 'SUMSQ';
6454
                        break;
6455
                    case 322:
6456
                        $function = 'KURT';
6457
                        break;
6458
                    case 323:
6459
                        $function = 'SKEW';
6460
                        break;
6461
                    case 324:
6462
                        $function = 'ZTEST';
6463
                        break;
6464
                    case 329:
6465
                        $function = 'PERCENTRANK';
6466
                        break;
6467
                    case 330:
6468
                        $function = 'MODE';
6469
                        break;
6470
                    case 336:
6471
                        $function = 'CONCATENATE';
6472
                        break;
6473
                    case 344:
6474
                        $function = 'SUBTOTAL';
6475
                        break;
6476
                    case 345:
6477
                        $function = 'SUMIF';
6478
                        break;
6479
                    case 354:
6480
                        $function = 'ROMAN';
6481
                        break;
6482
                    case 358:
6483
                        $function = 'GETPIVOTDATA';
6484
                        break;
6485
                    case 359:
6486
                        $function = 'HYPERLINK';
6487
                        break;
6488
                    case 361:
6489
                        $function = 'AVERAGEA';
6490
                        break;
6491
                    case 362:
6492
                        $function = 'MAXA';
6493
                        break;
6494
                    case 363:
6495
                        $function = 'MINA';
6496
                        break;
6497
                    case 364:
6498
                        $function = 'STDEVPA';
6499
                        break;
6500
                    case 365:
6501
                        $function = 'VARPA';
6502
                        break;
6503
                    case 366:
6504
                        $function = 'STDEVA';
6505
                        break;
6506
                    case 367:
6507
                        $function = 'VARA';
6508
                        break;
6509
                    default:
6510
                        throw new Exception('Unrecognized function in formula');
6511
                        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...
6512
                }
6513
                $data = ['function' => $function, 'args' => $args];
6514
                break;
6515
            case 0x23:    //    index to defined name
6516
            case 0x43:
6517
            case 0x63:
6518
                $name = 'tName';
6519
                $size = 5;
6520
                // offset: 1; size: 2; one-based index to definedname record
6521
                $definedNameIndex = self::getInt2d($formulaData, 1) - 1;
6522
                // offset: 2; size: 2; not used
6523
                $data = $this->definedname[$definedNameIndex]['name'];
6524
                break;
6525
            case 0x24:    //    single cell reference e.g. A5
6526
            case 0x44:
6527
            case 0x64:
6528
                $name = 'tRef';
6529
                $size = 5;
6530
                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
6531
                break;
6532
            case 0x25:    //    cell range reference to cells in the same sheet (2d)
6533
            case 0x45:
6534
            case 0x65:
6535
                $name = 'tArea';
6536
                $size = 9;
6537
                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
6538
                break;
6539
            case 0x26:    //    Constant reference sub-expression
6540
            case 0x46:
6541 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...
6542
                $name = 'tMemArea';
6543
                // offset: 1; size: 4; not used
6544
                // offset: 5; size: 2; size of the following subexpression
6545
                $subSize = self::getInt2d($formulaData, 5);
6546
                $size = 7 + $subSize;
6547
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6548
                break;
6549
            case 0x27:    //    Deleted constant reference sub-expression
6550
            case 0x47:
6551 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...
6552
                $name = 'tMemErr';
6553
                // offset: 1; size: 4; not used
6554
                // offset: 5; size: 2; size of the following subexpression
6555
                $subSize = self::getInt2d($formulaData, 5);
6556
                $size = 7 + $subSize;
6557
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6558
                break;
6559
            case 0x29:    //    Variable reference sub-expression
6560
            case 0x49:
6561 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...
6562
                $name = 'tMemFunc';
6563
                // offset: 1; size: 2; size of the following sub-expression
6564
                $subSize = self::getInt2d($formulaData, 1);
6565
                $size = 3 + $subSize;
6566
                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
6567
                break;
6568
            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
6569
            case 0x4C:
6570
            case 0x6C:
6571
                $name = 'tRefN';
6572
                $size = 5;
6573
                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
6574
                break;
6575
            case 0x2D:    //    Relative 2d range reference
6576
            case 0x4D:
6577
            case 0x6D:
6578
                $name = 'tAreaN';
6579
                $size = 9;
6580
                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
6581
                break;
6582
            case 0x39:    //    External name
6583
            case 0x59:
6584
            case 0x79:
6585
                $name = 'tNameX';
6586
                $size = 7;
6587
                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
6588
                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
6589
                $index = self::getInt2d($formulaData, 3);
6590
                // assume index is to EXTERNNAME record
6591
                $data = $this->externalNames[$index - 1]['name'];
6592
                // offset: 5; size: 2; not used
6593
                break;
6594
            case 0x3A:    //    3d reference to cell
6595
            case 0x5A:
6596 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...
6597
                $name = 'tRef3d';
6598
                $size = 7;
6599
6600
                try {
6601
                    // offset: 1; size: 2; index to REF entry
6602
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6603
                    // offset: 3; size: 4; cell address
6604
                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
6605
6606
                    $data = "$sheetRange!$cellAddress";
6607
                } catch (\PhpSpreadsheet\Exception $e) {
6608
                    // deleted sheet reference
6609
                    $data = '#REF!';
6610
                }
6611
                break;
6612
            case 0x3B:    //    3d reference to cell range
6613
            case 0x5B:
6614 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...
6615
                $name = 'tArea3d';
6616
                $size = 11;
6617
6618
                try {
6619
                    // offset: 1; size: 2; index to REF entry
6620
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6621
                    // offset: 3; size: 8; cell address
6622
                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
6623
6624
                    $data = "$sheetRange!$cellRangeAddress";
6625
                } catch (\PhpSpreadsheet\Exception $e) {
6626
                    // deleted sheet reference
6627
                    $data = '#REF!';
6628
                }
6629
                break;
6630
            // Unknown cases    // don't know how to deal with
6631
            default:
6632
                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
6633
                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...
6634
        }
6635
6636
        return [
6637
            'id' => $id,
6638
            'name' => $name,
6639
            'size' => $size,
6640
            'data' => $data,
6641
        ];
6642
    }
6643
6644
    /**
6645
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
6646
     * section 3.3.4
6647
     *
6648
     * @param string $cellAddressStructure
6649
     * @return string
6650
     */
6651
    private function readBIFF8CellAddress($cellAddressStructure)
6652
    {
6653
        // 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...
6654
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6655
6656
        // offset: 2; size: 2; index to column or column offset + relative flags
6657
        // bit: 7-0; mask 0x00FF; column index
6658
        $column = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($cellAddressStructure, 2));
6659
6660
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6661
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
6662
            $column = '$' . $column;
6663
        }
6664
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6665
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6666
            $row = '$' . $row;
6667
        }
6668
6669
        return $column . $row;
6670
    }
6671
6672
    /**
6673
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6674
     * to indicate offsets from a base cell
6675
     * section 3.3.4
6676
     *
6677
     * @param string $cellAddressStructure
6678
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
6679
     * @return string
6680
     */
6681
    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
6682
    {
6683
        list($baseCol, $baseRow) = \PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6684
        $baseCol = \PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6685
6686
        // 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...
6687
        $rowIndex = self::getInt2d($cellAddressStructure, 0);
6688
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6689
6690
        // offset: 2; size: 2; index to column or column offset + relative flags
6691
        // bit: 7-0; mask 0x00FF; column index
6692
        $colIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
6693
6694
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6695 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...
6696
            $column = \PhpSpreadsheet\Cell::stringFromColumnIndex($colIndex);
6697
            $column = '$' . $column;
6698
        } else {
6699
            $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
6700
            $column = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $colIndex);
6701
        }
6702
6703
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6704
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6705
            $row = '$' . $row;
6706
        } else {
6707
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
6708
            $row = $baseRow + $rowIndex;
6709
        }
6710
6711
        return $column . $row;
6712
    }
6713
6714
    /**
6715
     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
6716
     * always fixed range
6717
     * section 2.5.14
6718
     *
6719
     * @param string $subData
6720
     * @throws Exception
6721
     * @return string
6722
     */
6723
    private function readBIFF5CellRangeAddressFixed($subData)
6724
    {
6725
        // offset: 0; size: 2; index to first row
6726
        $fr = self::getInt2d($subData, 0) + 1;
6727
6728
        // offset: 2; size: 2; index to last row
6729
        $lr = self::getInt2d($subData, 2) + 1;
6730
6731
        // offset: 4; size: 1; index to first column
6732
        $fc = ord($subData{4});
6733
6734
        // offset: 5; size: 1; index to last column
6735
        $lc = ord($subData{5});
6736
6737
        // check values
6738
        if ($fr > $lr || $fc > $lc) {
6739
            throw new Exception('Not a cell range address');
6740
        }
6741
6742
        // column index to letter
6743
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6744
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6745
6746
        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...
6747
            return "$fc$fr";
6748
        }
6749
6750
        return "$fc$fr:$lc$lr";
6751
    }
6752
6753
    /**
6754
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
6755
     * always fixed range
6756
     * section 2.5.14
6757
     *
6758
     * @param string $subData
6759
     * @throws Exception
6760
     * @return string
6761
     */
6762
    private function readBIFF8CellRangeAddressFixed($subData)
6763
    {
6764
        // offset: 0; size: 2; index to first row
6765
        $fr = self::getInt2d($subData, 0) + 1;
6766
6767
        // offset: 2; size: 2; index to last row
6768
        $lr = self::getInt2d($subData, 2) + 1;
6769
6770
        // offset: 4; size: 2; index to first column
6771
        $fc = self::getInt2d($subData, 4);
6772
6773
        // offset: 6; size: 2; index to last column
6774
        $lc = self::getInt2d($subData, 6);
6775
6776
        // check values
6777
        if ($fr > $lr || $fc > $lc) {
6778
            throw new Exception('Not a cell range address');
6779
        }
6780
6781
        // column index to letter
6782
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6783
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6784
6785
        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...
6786
            return "$fc$fr";
6787
        }
6788
6789
        return "$fc$fr:$lc$lr";
6790
    }
6791
6792
    /**
6793
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
6794
     * there are flags indicating whether column/row index is relative
6795
     * section 3.3.4
6796
     *
6797
     * @param string $subData
6798
     * @return string
6799
     */
6800
    private function readBIFF8CellRangeAddress($subData)
6801
    {
6802
        // todo: if cell range is just a single cell, should this funciton
6803
        // not just return e.g. 'A1' and not 'A1:A1' ?
6804
6805
        // 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...
6806
            $fr = self::getInt2d($subData, 0) + 1;
6807
6808
        // 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...
6809
            $lr = self::getInt2d($subData, 2) + 1;
6810
6811
        // offset: 4; size: 2; index to first column or column offset + relative flags
6812
6813
        // bit: 7-0; mask 0x00FF; column index
6814
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 4));
6815
6816
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6817
        if (!(0x4000 & self::getInt2d($subData, 4))) {
6818
            $fc = '$' . $fc;
6819
        }
6820
6821
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6822
        if (!(0x8000 & self::getInt2d($subData, 4))) {
6823
            $fr = '$' . $fr;
6824
        }
6825
6826
        // offset: 6; size: 2; index to last column or column offset + relative flags
6827
6828
        // bit: 7-0; mask 0x00FF; column index
6829
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 6));
6830
6831
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6832
        if (!(0x4000 & self::getInt2d($subData, 6))) {
6833
            $lc = '$' . $lc;
6834
        }
6835
6836
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6837
        if (!(0x8000 & self::getInt2d($subData, 6))) {
6838
            $lr = '$' . $lr;
6839
        }
6840
6841
        return "$fc$fr:$lc$lr";
6842
    }
6843
6844
    /**
6845
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6846
     * to indicate offsets from a base cell
6847
     * section 3.3.4
6848
     *
6849
     * @param string $subData
6850
     * @param string $baseCell Base cell
6851
     * @return string Cell range address
6852
     */
6853
    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
6854
    {
6855
        list($baseCol, $baseRow) = \PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6856
        $baseCol = \PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6857
6858
        // TODO: if cell range is just a single cell, should this funciton
6859
        // not just return e.g. 'A1' and not 'A1:A1' ?
6860
6861
        // offset: 0; size: 2; first row
6862
        $frIndex = self::getInt2d($subData, 0); // adjust below
6863
6864
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
6865
        $lrIndex = self::getInt2d($subData, 2); // adjust below
6866
6867
        // offset: 4; size: 2; first column with relative/absolute flags
6868
6869
        // bit: 7-0; mask 0x00FF; column index
6870
        $fcIndex = 0x00FF & self::getInt2d($subData, 4);
6871
6872
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6873 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...
6874
            // absolute column index
6875
            $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fcIndex);
6876
            $fc = '$' . $fc;
6877
        } else {
6878
            // column offset
6879
            $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
6880
            $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $fcIndex);
6881
        }
6882
6883
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6884 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...
6885
            // absolute row index
6886
            $fr = $frIndex + 1;
6887
            $fr = '$' . $fr;
6888
        } else {
6889
            // row offset
6890
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
6891
            $fr = $baseRow + $frIndex;
6892
        }
6893
6894
        // offset: 6; size: 2; last column with relative/absolute flags
6895
6896
        // bit: 7-0; mask 0x00FF; column index
6897
        $lcIndex = 0x00FF & self::getInt2d($subData, 6);
6898
        $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6899
        $lc = \PhpSpreadsheet\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...
6900
6901
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6902 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...
6903
            // absolute column index
6904
            $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lcIndex);
6905
            $lc = '$' . $lc;
6906
        } else {
6907
            // column offset
6908
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6909
            $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
6910
        }
6911
6912
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6913 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...
6914
            // absolute row index
6915
            $lr = $lrIndex + 1;
6916
            $lr = '$' . $lr;
6917
        } else {
6918
            // row offset
6919
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
6920
            $lr = $baseRow + $lrIndex;
6921
        }
6922
6923
        return "$fc$fr:$lc$lr";
6924
    }
6925
6926
    /**
6927
     * Read BIFF8 cell range address list
6928
     * section 2.5.15
6929
     *
6930
     * @param string $subData
6931
     * @return array
6932
     */
6933 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...
6934
    {
6935
        $cellRangeAddresses = [];
6936
6937
        // offset: 0; size: 2; number of the following cell range addresses
6938
        $nm = self::getInt2d($subData, 0);
6939
6940
        $offset = 2;
6941
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
6942
        for ($i = 0; $i < $nm; ++$i) {
6943
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
6944
            $offset += 8;
6945
        }
6946
6947
        return [
6948
            'size' => 2 + 8 * $nm,
6949
            'cellRangeAddresses' => $cellRangeAddresses,
6950
        ];
6951
    }
6952
6953
    /**
6954
     * Read BIFF5 cell range address list
6955
     * section 2.5.15
6956
     *
6957
     * @param string $subData
6958
     * @return array
6959
     */
6960 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...
6961
    {
6962
        $cellRangeAddresses = [];
6963
6964
        // offset: 0; size: 2; number of the following cell range addresses
6965
        $nm = self::getInt2d($subData, 0);
6966
6967
        $offset = 2;
6968
        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
6969
        for ($i = 0; $i < $nm; ++$i) {
6970
            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
6971
            $offset += 6;
6972
        }
6973
6974
        return [
6975
            'size' => 2 + 6 * $nm,
6976
            'cellRangeAddresses' => $cellRangeAddresses,
6977
        ];
6978
    }
6979
6980
    /**
6981
     * Get a sheet range like Sheet1:Sheet3 from REF index
6982
     * Note: If there is only one sheet in the range, one gets e.g Sheet1
6983
     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
6984
     * in which case an Exception is thrown
6985
     *
6986
     * @param int $index
6987
     * @throws Exception
6988
     * @return string|false
6989
     */
6990
    private function readSheetRangeByRefIndex($index)
6991
    {
6992
        if (isset($this->ref[$index])) {
6993
            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
6994
6995
            switch ($type) {
6996
                case 'internal':
6997
                    // check if we have a deleted 3d reference
6998
                    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...
6999
                        throw new Exception('Deleted sheet reference');
7000
                    }
7001
7002
                    // we have normal sheet range (collapsed or uncollapsed)
7003
                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
7004
                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
7005
7006
                    if ($firstSheetName == $lastSheetName) {
7007
                        // collapsed sheet range
7008
                        $sheetRange = $firstSheetName;
7009
                    } else {
7010
                        $sheetRange = "$firstSheetName:$lastSheetName";
7011
                    }
7012
7013
                    // escape the single-quotes
7014
                    $sheetRange = str_replace("'", "''", $sheetRange);
7015
7016
                    // if there are special characters, we need to enclose the range in single-quotes
7017
                    // todo: check if we have identified the whole set of special characters
7018
                    // it seems that the following characters are not accepted for sheet names
7019
                    // and we may assume that they are not present: []*/:\?
7020
                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
7021
                        $sheetRange = "'$sheetRange'";
7022
                    }
7023
7024
                    return $sheetRange;
7025
                    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...
7026
                default:
7027
                    // TODO: external sheet support
7028
                    throw new Exception('Excel5 reader only supports internal sheets in fomulas');
7029
                    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...
7030
            }
7031
        }
7032
7033
        return false;
7034
    }
7035
7036
    /**
7037
     * read BIFF8 constant value array from array data
7038
     * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
7039
     * section 2.5.8
7040
     *
7041
     * @param string $arrayData
7042
     * @return array
7043
     */
7044
    private static function readBIFF8ConstantArray($arrayData)
7045
    {
7046
        // offset: 0; size: 1; number of columns decreased by 1
7047
        $nc = ord($arrayData[0]);
7048
7049
        // offset: 1; size: 2; number of rows decreased by 1
7050
        $nr = self::getInt2d($arrayData, 1);
7051
        $size = 3; // initialize
7052
        $arrayData = substr($arrayData, 3);
7053
7054
        // 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...
7055
        $matrixChunks = [];
7056
        for ($r = 1; $r <= $nr + 1; ++$r) {
7057
            $items = [];
7058
            for ($c = 1; $c <= $nc + 1; ++$c) {
7059
                $constant = self::readBIFF8Constant($arrayData);
7060
                $items[] = $constant['value'];
7061
                $arrayData = substr($arrayData, $constant['size']);
7062
                $size += $constant['size'];
7063
            }
7064
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
7065
        }
7066
        $matrix = '{' . implode(';', $matrixChunks) . '}';
7067
7068
        return [
7069
            'value' => $matrix,
7070
            'size' => $size,
7071
        ];
7072
    }
7073
7074
    /**
7075
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
7076
     * section 2.5.7
7077
     * returns e.g. array('value' => '5', 'size' => 9)
7078
     *
7079
     * @param string $valueData
7080
     * @return array
7081
     */
7082
    private static function readBIFF8Constant($valueData)
7083
    {
7084
        // offset: 0; size: 1; identifier for type of constant
7085
        $identifier = ord($valueData[0]);
7086
7087
        switch ($identifier) {
7088
            case 0x00: // empty constant (what is this?)
7089
                $value = '';
7090
                $size = 9;
7091
                break;
7092
            case 0x01: // number
7093
                // offset: 1; size: 8; IEEE 754 floating-point value
7094
                $value = self::extractNumber(substr($valueData, 1, 8));
7095
                $size = 9;
7096
                break;
7097
            case 0x02: // string value
7098
                // offset: 1; size: var; Unicode string, 16-bit string length
7099
                $string = self::readUnicodeStringLong(substr($valueData, 1));
7100
                $value = '"' . $string['value'] . '"';
7101
                $size = 1 + $string['size'];
7102
                break;
7103
            case 0x04: // boolean
7104
                // 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...
7105
                if (ord($valueData[1])) {
7106
                    $value = 'TRUE';
7107
                } else {
7108
                    $value = 'FALSE';
7109
                }
7110
                $size = 9;
7111
                break;
7112
            case 0x10: // error code
7113
                // offset: 1; size: 1; error code
7114
                $value = Excel5\ErrorCode::lookup(ord($valueData[1]));
7115
                $size = 9;
7116
                break;
7117
        }
7118
7119
        return [
7120
            '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...
7121
            '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...
7122
        ];
7123
    }
7124
7125
    /**
7126
     * Extract RGB color
7127
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
7128
     *
7129
     * @param string $rgb Encoded RGB value (4 bytes)
7130
     * @return array
7131
     */
7132
    private static function readRGB($rgb)
7133
    {
7134
        // offset: 0; size 1; Red component
7135
        $r = ord($rgb{0});
7136
7137
        // offset: 1; size: 1; Green component
7138
        $g = ord($rgb{1});
7139
7140
        // offset: 2; size: 1; Blue component
7141
        $b = ord($rgb{2});
7142
7143
        // HEX notation, e.g. 'FF00FC'
7144
        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
7145
7146
        return ['rgb' => $rgb];
7147
    }
7148
7149
    /**
7150
     * Read byte string (8-bit string length)
7151
     * OpenOffice documentation: 2.5.2
7152
     *
7153
     * @param string $subData
7154
     * @return array
7155
     */
7156 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...
7157
    {
7158
        // offset: 0; size: 1; length of the string (character count)
7159
        $ln = ord($subData[0]);
7160
7161
        // 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...
7162
        $value = $this->decodeCodepage(substr($subData, 1, $ln));
7163
7164
        return [
7165
            'value' => $value,
7166
            'size' => 1 + $ln, // size in bytes of data structure
7167
        ];
7168
    }
7169
7170
    /**
7171
     * Read byte string (16-bit string length)
7172
     * OpenOffice documentation: 2.5.2
7173
     *
7174
     * @param string $subData
7175
     * @return array
7176
     */
7177 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...
7178
    {
7179
        // offset: 0; size: 2; length of the string (character count)
7180
        $ln = self::getInt2d($subData, 0);
7181
7182
        // 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...
7183
        $value = $this->decodeCodepage(substr($subData, 2));
7184
7185
        //return $string;
7186
        return [
7187
            'value' => $value,
7188
            'size' => 2 + $ln, // size in bytes of data structure
7189
        ];
7190
    }
7191
7192
    /**
7193
     * Extracts an Excel Unicode short string (8-bit string length)
7194
     * OpenOffice documentation: 2.5.3
7195
     * function will automatically find out where the Unicode string ends.
7196
     *
7197
     * @param string $subData
7198
     * @return array
7199
     */
7200 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...
7201
    {
7202
        $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...
7203
7204
        // offset: 0: size: 1; length of the string (character count)
7205
        $characterCount = ord($subData[0]);
7206
7207
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7208
7209
        // add 1 for the string length
7210
        $string['size'] += 1;
7211
7212
        return $string;
7213
    }
7214
7215
    /**
7216
     * Extracts an Excel Unicode long string (16-bit string length)
7217
     * OpenOffice documentation: 2.5.3
7218
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7219
     *
7220
     * @param string $subData
7221
     * @return array
7222
     */
7223 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...
7224
    {
7225
        $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...
7226
7227
        // offset: 0: size: 2; length of the string (character count)
7228
        $characterCount = self::getInt2d($subData, 0);
7229
7230
        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7231
7232
        // add 2 for the string length
7233
        $string['size'] += 2;
7234
7235
        return $string;
7236
    }
7237
7238
    /**
7239
     * Read Unicode string with no string length field, but with known character count
7240
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7241
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
7242
     *
7243
     * @param string $subData
7244
     * @param int $characterCount
7245
     * @return array
7246
     */
7247
    private static function readUnicodeString($subData, $characterCount)
7248
    {
7249
        $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...
7250
7251
        // offset: 0: size: 1; option flags
7252
        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
7253
        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7254
7255
        // bit: 2; mask: 0x04; Asian phonetic settings
7256
        $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...
7257
7258
        // bit: 3; mask: 0x08; Rich-Text settings
7259
        $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...
7260
7261
        // 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...
7262
        // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
7263
        // needs to be fixed
7264
        $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
7265
7266
        return [
7267
            'value' => $value,
7268
            'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
7269
        ];
7270
    }
7271
7272
    /**
7273
     * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
7274
     * Example:  hello"world  -->  "hello""world"
7275
     *
7276
     * @param string $value UTF-8 encoded string
7277
     * @return string
7278
     */
7279
    private static function UTF8toExcelDoubleQuoted($value)
7280
    {
7281
        return '"' . str_replace('"', '""', $value) . '"';
7282
    }
7283
7284
    /**
7285
     * Reads first 8 bytes of a string and return IEEE 754 float
7286
     *
7287
     * @param string $data Binary string that is at least 8 bytes long
7288
     * @return float
7289
     */
7290
    private static function extractNumber($data)
7291
    {
7292
        $rknumhigh = self::getInt4d($data, 4);
7293
        $rknumlow = self::getInt4d($data, 0);
7294
        $sign = ($rknumhigh & 0x80000000) >> 31;
7295
        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
7296
        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
7297
        $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
7298
        $mantissalow2 = ($rknumlow & 0x7fffffff);
7299
        $value = $mantissa / pow(2, (20 - $exp));
7300
7301
        if ($mantissalow1 != 0) {
7302
            $value += 1 / pow(2, (21 - $exp));
7303
        }
7304
7305
        $value += $mantissalow2 / pow(2, (52 - $exp));
7306
        if ($sign) {
7307
            $value *= -1;
7308
        }
7309
7310
        return $value;
7311
    }
7312
7313
    /**
7314
     * @param int $rknum
7315
     */
7316
    private static function getIEEE754($rknum)
7317
    {
7318
        if (($rknum & 0x02) != 0) {
7319
            $value = $rknum >> 2;
7320
        } else {
7321
            // changes by mmp, info on IEEE754 encoding from
7322
            // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
7323
            // The RK format calls for using only the most significant 30 bits
7324
            // of the 64 bit floating point value. The other 34 bits are assumed
7325
            // to be 0 so we use the upper 30 bits of $rknum as follows...
7326
            $sign = ($rknum & 0x80000000) >> 31;
7327
            $exp = ($rknum & 0x7ff00000) >> 20;
7328
            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
7329
            $value = $mantissa / pow(2, (20 - ($exp - 1023)));
7330
            if ($sign) {
7331
                $value = -1 * $value;
7332
            }
7333
            //end of changes by mmp
7334
        }
7335
        if (($rknum & 0x01) != 0) {
7336
            $value /= 100;
7337
        }
7338
7339
        return $value;
7340
    }
7341
7342
    /**
7343
     * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
7344
     *
7345
     * @param string $string
7346
     * @param bool $compressed
7347
     * @return string
7348
     */
7349
    private static function encodeUTF16($string, $compressed = false)
7350
    {
7351
        if ($compressed) {
7352
            $string = self::uncompressByteString($string);
7353
        }
7354
7355
        return \PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
7356
    }
7357
7358
    /**
7359
     * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
7360
     *
7361
     * @param string $string
7362
     * @return string
7363
     */
7364
    private static function uncompressByteString($string)
7365
    {
7366
        $uncompressedString = '';
7367
        $strLen = strlen($string);
7368
        for ($i = 0; $i < $strLen; ++$i) {
7369
            $uncompressedString .= $string[$i] . "\0";
7370
        }
7371
7372
        return $uncompressedString;
7373
    }
7374
7375
    /**
7376
     * Convert string to UTF-8. Only used for BIFF5.
7377
     *
7378
     * @param string $string
7379
     * @return string
7380
     */
7381
    private function decodeCodepage($string)
7382
    {
7383
        return \PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
7384
    }
7385
7386
    /**
7387
     * Read 16-bit unsigned integer
7388
     *
7389
     * @param string $data
7390
     * @param int $pos
7391
     * @return int
7392
     */
7393
    public static function getInt2d($data, $pos)
7394
    {
7395
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
7396
    }
7397
7398
    /**
7399
     * Read 32-bit signed integer
7400
     *
7401
     * @param string $data
7402
     * @param int $pos
7403
     * @return int
7404
     */
7405 View Code Duplication
    public static function getInt4d($data, $pos)
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...
7406
    {
7407
        // FIX: represent numbers correctly on 64-bit system
7408
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
7409
        // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
7410
        $_or_24 = ord($data[$pos + 3]);
7411
        if ($_or_24 >= 128) {
7412
            // negative number
7413
            $_ord_24 = -abs((256 - $_or_24) << 24);
7414
        } else {
7415
            $_ord_24 = ($_or_24 & 127) << 24;
7416
        }
7417
7418
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
7419
    }
7420
7421
    private function parseRichText($is = '')
7422
    {
7423
        $value = new \PhpSpreadsheet\RichText();
7424
        $value->createText($is);
7425
7426
        return $value;
7427
    }
7428
}
7429