Completed
Push — develop ( 03f96a...8c66af )
by Adrien
19:36
created

Xls::readImData()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 69
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 35
nc 7
nop 0
dl 0
loc 69
ccs 0
cts 34
cp 0
crap 42
rs 8.56
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
642 4
        $this->mapCellXfIndex = [];
643 4
        $this->mapCellStyleXfIndex = [];
644
645
        // Parse Workbook Global Substream
646 4
        while ($this->pos < $this->dataSize) {
647 4
            $code = self::getInt2d($this->data, $this->pos);
648
649
            switch ($code) {
650 4
                case self::XLS_TYPE_BOF:
651 4
                    $this->readBof();
652 4
                    break;
653 4
                case self::XLS_TYPE_FILEPASS:
654
                    $this->readFilepass();
655
                    break;
656 4
                case self::XLS_TYPE_CODEPAGE:
657 4
                    $this->readCodepage();
658 4
                    break;
659 4
                case self::XLS_TYPE_DATEMODE:
660 4
                    $this->readDateMode();
661 4
                    break;
662 4
                case self::XLS_TYPE_FONT:
663 4
                    $this->readFont();
664 4
                    break;
665 4
                case self::XLS_TYPE_FORMAT:
666 4
                    $this->readFormat();
667 4
                    break;
668 4
                case self::XLS_TYPE_XF:
669 4
                    $this->readXf();
670 4
                    break;
671 4
                case self::XLS_TYPE_XFEXT:
672 3
                    $this->readXfExt();
673 3
                    break;
674 4
                case self::XLS_TYPE_STYLE:
675 4
                    $this->readStyle();
676 4
                    break;
677 4
                case self::XLS_TYPE_PALETTE:
678 2
                    $this->readPalette();
679 2
                    break;
680 4
                case self::XLS_TYPE_SHEET:
681 4
                    $this->readSheet();
682 4
                    break;
683 4
                case self::XLS_TYPE_EXTERNALBOOK:
684 3
                    $this->readExternalBook();
685 3
                    break;
686 4
                case self::XLS_TYPE_EXTERNNAME:
687
                    $this->readExternName();
688
                    break;
689 4
                case self::XLS_TYPE_EXTERNSHEET:
690 3
                    $this->readExternSheet();
691 3
                    break;
692 4
                case self::XLS_TYPE_DEFINEDNAME:
693 1
                    $this->readDefinedName();
694 1
                    break;
695 4
                case self::XLS_TYPE_MSODRAWINGGROUP:
696 3
                    $this->readMsoDrawingGroup();
697 3
                    break;
698 4
                case self::XLS_TYPE_SST:
699 4
                    $this->readSst();
700 4
                    break;
701 4
                case self::XLS_TYPE_EOF:
702 4
                    $this->readDefault();
703 4
                    break 2;
704
                default:
705 4
                    $this->readDefault();
706 4
                    break;
707
            }
708
        }
709
710
        // Resolve indexed colors for font, fill, and border colors
711
        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
712 4
        if (!$this->readDataOnly) {
713 4
            foreach ($this->objFonts as $objFont) {
714 4 View Code Duplication
                if (isset($objFont->colorIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
715 4
                    $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
716 4
                    $objFont->getColor()->setRGB($color['rgb']);
717
                }
718
            }
719
720 4
            foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
721
                // fill start and end color
722 4
                $fill = $objStyle->getFill();
723
724 4
                if (isset($fill->startcolorIndex)) {
725 4
                    $startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

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

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

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

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

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

Loading history...
730 4
                    $fill->getEndColor()->setRGB($endColor['rgb']);
731
                }
732
733
                // border colors
734 4
                $top = $objStyle->getBorders()->getTop();
735 4
                $right = $objStyle->getBorders()->getRight();
736 4
                $bottom = $objStyle->getBorders()->getBottom();
737 4
                $left = $objStyle->getBorders()->getLeft();
738 4
                $diagonal = $objStyle->getBorders()->getDiagonal();
739
740 4 View Code Duplication
                if (isset($top->colorIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
741 4
                    $borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version);
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpOffice\PhpSpreadsheet\Style\Border.

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

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

Loading history...
742 4
                    $top->getColor()->setRGB($borderTopColor['rgb']);
743
                }
744 4 View Code Duplication
                if (isset($right->colorIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
745 4
                    $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
746 4
                    $right->getColor()->setRGB($borderRightColor['rgb']);
747
                }
748 4 View Code Duplication
                if (isset($bottom->colorIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
749 4
                    $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
750 4
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
751
                }
752 4 View Code Duplication
                if (isset($left->colorIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
753 4
                    $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
754 4
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
755
                }
756 4 View Code Duplication
                if (isset($diagonal->colorIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
757 4
                    $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
758 4
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
759
                }
760
            }
761
        }
762
763
        // treat MSODRAWINGGROUP records, workbook-level Escher
764 4
        if (!$this->readDataOnly && $this->drawingGroupData) {
765 2
            $escherWorkbook = new \PhpOffice\PhpSpreadsheet\Shared\Escher();
766 2
            $reader = new Xls\Escher($escherWorkbook);
767 2
            $escherWorkbook = $reader->load($this->drawingGroupData);
768
        }
769
770
        // Parse the individual sheets
771 4
        foreach ($this->sheets as $sheet) {
772 4
            if ($sheet['sheetType'] != 0x00) {
773
                // 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...
774
                continue;
775
            }
776
777
            // check if sheet should be skipped
778 4
            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
779
                continue;
780
            }
781
782
            // add sheet to PhpSpreadsheet object
783 4
            $this->phpSheet = $this->spreadsheet->createSheet();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->spreadsheet->createSheet() of type object<PhpOffice\PhpSpreadsheet\Worksheet> is incompatible with the declared type object<PhpOffice\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...
784
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
785
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
786
            //        name in line with the formula, not the reverse
787 4
            $this->phpSheet->setTitle($sheet['name'], false);
788 4
            $this->phpSheet->setSheetState($sheet['sheetState']);
789
790 4
            $this->pos = $sheet['offset'];
791
792
            // Initialize isFitToPages. May change after reading SHEETPR record.
793 4
            $this->isFitToPages = false;
794
795
            // Initialize drawingData
796 4
            $this->drawingData = '';
797
798
            // Initialize objs
799 4
            $this->objs = [];
800
801
            // Initialize shared formula parts
802 4
            $this->sharedFormulaParts = [];
803
804
            // Initialize shared formulas
805 4
            $this->sharedFormulas = [];
806
807
            // Initialize text objs
808 4
            $this->textObjects = [];
809
810
            // Initialize cell annotations
811 4
            $this->cellNotes = [];
812 4
            $this->textObjRef = -1;
0 ignored issues
show
Bug introduced by
The property textObjRef does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
813
814 4
            while ($this->pos <= $this->dataSize - 4) {
815 4
                $code = self::getInt2d($this->data, $this->pos);
816
817
                switch ($code) {
818 4
                    case self::XLS_TYPE_BOF:
819 4
                        $this->readBof();
820 4
                        break;
821 4
                    case self::XLS_TYPE_PRINTGRIDLINES:
822 4
                        $this->readPrintGridlines();
823 4
                        break;
824 4
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
825 3
                        $this->readDefaultRowHeight();
826 3
                        break;
827 4
                    case self::XLS_TYPE_SHEETPR:
828 4
                        $this->readSheetPr();
829 4
                        break;
830 4
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
831
                        $this->readHorizontalPageBreaks();
832
                        break;
833 4
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
834
                        $this->readVerticalPageBreaks();
835
                        break;
836 4
                    case self::XLS_TYPE_HEADER:
837 4
                        $this->readHeader();
838 4
                        break;
839 4
                    case self::XLS_TYPE_FOOTER:
840 4
                        $this->readFooter();
841 4
                        break;
842 4
                    case self::XLS_TYPE_HCENTER:
843 4
                        $this->readHcenter();
844 4
                        break;
845 4
                    case self::XLS_TYPE_VCENTER:
846 4
                        $this->readVcenter();
847 4
                        break;
848 4
                    case self::XLS_TYPE_LEFTMARGIN:
849 4
                        $this->readLeftMargin();
850 4
                        break;
851 4
                    case self::XLS_TYPE_RIGHTMARGIN:
852 4
                        $this->readRightMargin();
853 4
                        break;
854 4
                    case self::XLS_TYPE_TOPMARGIN:
855 4
                        $this->readTopMargin();
856 4
                        break;
857 4
                    case self::XLS_TYPE_BOTTOMMARGIN:
858 4
                        $this->readBottomMargin();
859 4
                        break;
860 4
                    case self::XLS_TYPE_PAGESETUP:
861 4
                        $this->readPageSetup();
862 4
                        break;
863 4
                    case self::XLS_TYPE_PROTECT:
864 1
                        $this->readProtect();
865 1
                        break;
866 4
                    case self::XLS_TYPE_SCENPROTECT:
867
                        $this->readScenProtect();
868
                        break;
869 4
                    case self::XLS_TYPE_OBJECTPROTECT:
870
                        $this->readObjectProtect();
871
                        break;
872 4
                    case self::XLS_TYPE_PASSWORD:
873
                        $this->readPassword();
874
                        break;
875 4
                    case self::XLS_TYPE_DEFCOLWIDTH:
876 4
                        $this->readDefColWidth();
877 4
                        break;
878 4
                    case self::XLS_TYPE_COLINFO:
879 3
                        $this->readColInfo();
880 3
                        break;
881 4
                    case self::XLS_TYPE_DIMENSION:
882 4
                        $this->readDefault();
883 4
                        break;
884 4
                    case self::XLS_TYPE_ROW:
885 3
                        $this->readRow();
886 3
                        break;
887 4
                    case self::XLS_TYPE_DBCELL:
888 3
                        $this->readDefault();
889 3
                        break;
890 4
                    case self::XLS_TYPE_RK:
891 1
                        $this->readRk();
892 1
                        break;
893 4
                    case self::XLS_TYPE_LABELSST:
894 4
                        $this->readLabelSst();
895 4
                        break;
896 4
                    case self::XLS_TYPE_MULRK:
897
                        $this->readMulRk();
898
                        break;
899 4
                    case self::XLS_TYPE_NUMBER:
900 1
                        $this->readNumber();
901 1
                        break;
902 4
                    case self::XLS_TYPE_FORMULA:
903 2
                        $this->readFormula();
904 2
                        break;
905 4
                    case self::XLS_TYPE_SHAREDFMLA:
906
                        $this->readSharedFmla();
907
                        break;
908 4
                    case self::XLS_TYPE_BOOLERR:
909
                        $this->readBoolErr();
910
                        break;
911 4
                    case self::XLS_TYPE_MULBLANK:
912 1
                        $this->readMulBlank();
913 1
                        break;
914 4
                    case self::XLS_TYPE_LABEL:
915
                        $this->readLabel();
916
                        break;
917 4
                    case self::XLS_TYPE_BLANK:
918 2
                        $this->readBlank();
919 2
                        break;
920 4
                    case self::XLS_TYPE_MSODRAWING:
921 2
                        $this->readMsoDrawing();
922 2
                        break;
923 4
                    case self::XLS_TYPE_OBJ:
924 2
                        $this->readObj();
925 2
                        break;
926 4
                    case self::XLS_TYPE_WINDOW2:
927 4
                        $this->readWindow2();
928 4
                        break;
929 4
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
930 4
                        $this->readPageLayoutView();
931 4
                        break;
932 4
                    case self::XLS_TYPE_SCL:
933
                        $this->readScl();
934
                        break;
935 4
                    case self::XLS_TYPE_PANE:
936
                        $this->readPane();
937
                        break;
938 4
                    case self::XLS_TYPE_SELECTION:
939 4
                        $this->readSelection();
940 4
                        break;
941 4
                    case self::XLS_TYPE_MERGEDCELLS:
942 2
                        $this->readMergedCells();
943 2
                        break;
944 4
                    case self::XLS_TYPE_HYPERLINK:
945 2
                        $this->readHyperLink();
946 2
                        break;
947 4
                    case self::XLS_TYPE_DATAVALIDATIONS:
948
                        $this->readDataValidations();
949
                        break;
950 4
                    case self::XLS_TYPE_DATAVALIDATION:
951
                        $this->readDataValidation();
952
                        break;
953 4
                    case self::XLS_TYPE_SHEETLAYOUT:
954 2
                        $this->readSheetLayout();
955 2
                        break;
956 4
                    case self::XLS_TYPE_SHEETPROTECTION:
957 4
                        $this->readSheetProtection();
958 4
                        break;
959 4
                    case self::XLS_TYPE_RANGEPROTECTION:
960 1
                        $this->readRangeProtection();
961 1
                        break;
962 4
                    case self::XLS_TYPE_NOTE:
963 1
                        $this->readNote();
964 1
                        break;
965
                    //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...
966 4
                    case self::XLS_TYPE_TXO:
967 1
                        $this->readTextObject();
968 1
                        break;
969 4
                    case self::XLS_TYPE_CONTINUE:
970
                        $this->readContinue();
971
                        break;
972 4
                    case self::XLS_TYPE_EOF:
973 4
                        $this->readDefault();
974 4
                        break 2;
975
                    default:
976 4
                        $this->readDefault();
977 4
                        break;
978
                }
979
            }
980
981
            // treat MSODRAWING records, sheet-level Escher
982 4
            if (!$this->readDataOnly && $this->drawingData) {
983 2
                $escherWorksheet = new \PhpOffice\PhpSpreadsheet\Shared\Escher();
984 2
                $reader = new Xls\Escher($escherWorksheet);
985 2
                $escherWorksheet = $reader->load($this->drawingData);
986
987
                // get all spContainers in one long array, so they can be mapped to OBJ records
988 2
                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
989
            }
990
991
            // treat OBJ records
992 4
            foreach ($this->objs as $n => $obj) {
993
                // the first shape container never has a corresponding OBJ record, hence $n + 1
994 2
                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
995 2
                    $spContainer = $allSpContainers[$n + 1];
996
997
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
998 2
                    if ($spContainer->getNestingLevel() > 1) {
999
                        continue;
1000
                    }
1001
1002
                    // calculate the width and height of the shape
1003 2
                    list($startColumn, $startRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($spContainer->getStartCoordinates());
1004 2
                    list($endColumn, $endRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($spContainer->getEndCoordinates());
1005
1006 2
                    $startOffsetX = $spContainer->getStartOffsetX();
1007 2
                    $startOffsetY = $spContainer->getStartOffsetY();
1008 2
                    $endOffsetX = $spContainer->getEndOffsetX();
1009 2
                    $endOffsetY = $spContainer->getEndOffsetY();
1010
1011 2
                    $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
1012 2
                    $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
1013
1014
                    // calculate offsetX and offsetY of the shape
1015 2
                    $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024;
1016 2
                    $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256;
1017
1018 2
                    switch ($obj['otObjType']) {
1019 2
                        case 0x19:
1020
                            // Note
1021 1
                            if (isset($this->cellNotes[$obj['idObjID']])) {
1022 1
                                $cellNote = $this->cellNotes[$obj['idObjID']];
0 ignored issues
show
Unused Code introduced by
$cellNote is not used, you could remove the assignment.

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

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

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

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

Loading history...
1023
1024 1 View Code Duplication
                                if (isset($this->textObjects[$obj['idObjID']])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1025 1
                                    $textObject = $this->textObjects[$obj['idObjID']];
1026 1
                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
1027
                                }
1028
                            }
1029 1
                            break;
1030 2
                        case 0x08:
1031
                            // picture
1032
                            // get index to BSE entry (1-based)
1033 2
                            $BSEindex = $spContainer->getOPT(0x0104);
1034
1035
                            // If there is no BSE Index, we will fail here and other fields are not read.
1036
                            // Fix by checking here.
1037
                            // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
1038
                            // More likely : a uncompatible picture
1039 2
                            if (!$BSEindex) {
1040
                                continue;
1041
                            }
1042
1043 2
                            $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...
1044 2
                            $BSE = $BSECollection[$BSEindex - 1];
1045 2
                            $blipType = $BSE->getBlipType();
1046
1047
                            // need check because some blip types are not supported by Escher reader such as EMF
1048 2
                            if ($blip = $BSE->getBlip()) {
1049 2
                                $ih = imagecreatefromstring($blip->getData());
1050 2
                                $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing();
1051 2
                                $drawing->setImageResource($ih);
1052
1053
                                // width, height, offsetX, offsetY
1054 2
                                $drawing->setResizeProportional(false);
1055 2
                                $drawing->setWidth($width);
1056 2
                                $drawing->setHeight($height);
1057 2
                                $drawing->setOffsetX($offsetX);
1058 2
                                $drawing->setOffsetY($offsetY);
1059
1060
                                switch ($blipType) {
1061 2
                                    case \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_JPEG:
1062 2
                                        $drawing->setRenderingFunction(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_JPEG);
1063 2
                                        $drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_JPEG);
1064 2
                                        break;
1065 2
                                    case \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_PNG:
1066 2
                                        $drawing->setRenderingFunction(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_PNG);
1067 2
                                        $drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_PNG);
1068 2
                                        break;
1069
                                }
1070
1071 2
                                $drawing->setWorksheet($this->phpSheet);
1072 2
                                $drawing->setCoordinates($spContainer->getStartCoordinates());
1073
                            }
1074 2
                            break;
1075
                        default:
1076
                            // other object type
1077 2
                            break;
1078
                    }
1079
                }
1080
            }
1081
1082
            // treat SHAREDFMLA records
1083 4
            if ($this->version == self::XLS_BIFF8) {
1084 4
                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
1085
                    list($column, $row) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($cell);
1086
                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
1087
                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
1088
                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
1089
                    }
1090
                }
1091
            }
1092
1093 4
            if (!empty($this->cellNotes)) {
1094 1
                foreach ($this->cellNotes as $note => $noteDetails) {
1095 1
                    if (!isset($noteDetails['objTextData'])) {
1096 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...
1097
                            $textObject = $this->textObjects[$note];
1098
                            $noteDetails['objTextData'] = $textObject;
1099
                        } else {
1100
                            $noteDetails['objTextData']['text'] = '';
1101
                        }
1102
                    }
1103 1
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
1104 4
                    $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
1105
                }
1106
            }
1107
        }
1108
1109
        // add the named ranges (defined names)
1110 4
        foreach ($this->definedname as $definedName) {
1111 1
            if ($definedName['isBuiltInName']) {
1112
                switch ($definedName['name']) {
1113
                    case pack('C', 0x06):
1114
                        // print area
1115
                        //    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...
1116
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1117
1118
                        $extractedRanges = [];
1119
                        foreach ($ranges as $range) {
1120
                            // $range should look like one of these
1121
                            //        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...
1122
                            //        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...
1123
                            $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
1124
                            $sheetName = trim($explodes[0], "'");
1125
                            if (count($explodes) == 2) {
1126
                                if (strpos($explodes[1], ':') === false) {
1127
                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
1128
                                }
1129
                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
1130
                            }
1131
                        }
1132
                        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...
1133
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
1134
                        }
1135
                        break;
1136
                    case pack('C', 0x07):
1137
                        // print titles (repeating rows)
1138
                        // Assuming BIFF8, there are 3 cases
1139
                        // 1. repeating rows
1140
                        //        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...
1141
                        //        rows 1-2 repeat
1142
                        // 2. repeating columns
1143
                        //        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...
1144
                        //        columns A-B repeat
1145
                        // 3. both repeating rows and repeating columns
1146
                        //        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...
1147
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1148
                        foreach ($ranges as $range) {
1149
                            // $range should look like this one of these
1150
                            //        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...
1151
                            //        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...
1152
                            $explodes = explode('!', $range);
1153
                            if (count($explodes) == 2) {
1154
                                if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
1155
                                    $extractedRange = $explodes[1];
1156
                                    $extractedRange = str_replace('$', '', $extractedRange);
1157
1158
                                    $coordinateStrings = explode(':', $extractedRange);
1159
                                    if (count($coordinateStrings) == 2) {
1160
                                        list($firstColumn, $firstRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[0]);
1161
                                        list($lastColumn, $lastRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[1]);
1162
1163
                                        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...
1164
                                            // then we have repeating rows
1165
                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
1166
                                        } 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...
1167
                                            // then we have repeating columns
1168
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
1169
                                        }
1170
                                    }
1171
                                }
1172
                            }
1173
                        }
1174
                        break;
1175
                }
1176
            } else {
1177
                // Extract range
1178 1
                $explodes = explode('!', $definedName['formula']);
1179
1180 1
                if (count($explodes) == 2) {
1181 1
                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1182 1
                        ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $docSheet is correct as $this->spreadsheet->getS...im($explodes[0], '\'')) (which targets PhpOffice\PhpSpreadsheet...sheet::getSheetByName()) seems to always return null.

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

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

}

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

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

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

Loading history...
1183 1
                        $extractedRange = $explodes[1];
1184 1
                        $extractedRange = str_replace('$', '', $extractedRange);
1185
1186 1
                        $localOnly = ($definedName['scope'] == 0) ? false : true;
1187
1188 1
                        $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
1189
1190 1
                        $this->spreadsheet->addNamedRange(new \PhpOffice\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...
1191
                    }
1192
                }
1193
                    //    Named Value
1194
                    //    TODO Provide support for named values
1195
            }
1196
        }
1197 4
        $this->data = null;
1198
1199 4
        return $this->spreadsheet;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->spreadsheet; (PhpOffice\PhpSpreadsheet\Spreadsheet) is incompatible with the return type declared by the interface PhpOffice\PhpSpreadsheet\Reader\IReader::load of type PhpOffice\PhpSpreadsheet\Reader\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...
1200
    }
1201
1202
    /**
1203
     * Read record data from stream, decrypting as required.
1204
     *
1205
     * @param string $data   Data stream to read from
1206
     * @param int    $pos    Position to start reading from
1207
     * @param int    $len    Record data length
1208
     *
1209
     * @return string Record data
1210
     */
1211 4
    private function readRecordData($data, $pos, $len)
1212
    {
1213 4
        $data = substr($data, $pos, $len);
1214
1215
        // File not encrypted, or record before encryption start point
1216 4
        if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
1217 4
            return $data;
1218
        }
1219
1220
        $recordData = '';
1221
        if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
1222
            $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
1223
            $block = floor($pos / self::REKEY_BLOCK);
1224
            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
1225
1226
            // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
1227
            // at a point earlier in the current block, re-use it as we can save some time.
1228
            if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
1229
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1230
                $step = $pos % self::REKEY_BLOCK;
1231
            } else {
1232
                $step = $pos - $this->rc4Pos;
1233
            }
1234
            $this->rc4Key->RC4(str_repeat("\0", $step));
1235
1236
            // Decrypt record data (re-keying at the end of every block)
1237
            while ($block != $endBlock) {
1238
                $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
1239
                $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
1240
                $data = substr($data, $step);
1241
                $pos += $step;
1242
                $len -= $step;
1243
                ++$block;
1244
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1245
            }
1246
            $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
1247
1248
            // Keep track of the position of this decryptor.
1249
            // We'll try and re-use it later if we can to speed things up
1250
            $this->rc4Pos = $pos + $len;
1251
        } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
1252
            throw new Exception('XOr encryption not supported');
1253
        }
1254
1255
        return $recordData;
1256
    }
1257
1258
    /**
1259
     * Use OLE reader to extract the relevant data streams from the OLE file.
1260
     *
1261
     * @param string $pFilename
1262
     */
1263 4
    private function loadOLE($pFilename)
1264
    {
1265
        // OLE reader
1266 4
        $ole = new \PhpOffice\PhpSpreadsheet\Shared\OLERead();
1267
        // get excel data,
1268 4
        $ole->read($pFilename);
1269
        // Get workbook data: workbook stream + sheet streams
1270 4
        $this->data = $ole->getStream($ole->wrkbook);
1271
        // Get summary information data
1272 4
        $this->summaryInformation = $ole->getStream($ole->summaryInformation);
1273
        // Get additional document summary information data
1274 4
        $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1275 4
    }
1276
1277
    /**
1278
     * Read summary information.
1279
     */
1280 4
    private function readSummaryInformation()
1281
    {
1282 4
        if (!isset($this->summaryInformation)) {
1283
            return;
1284
        }
1285
1286
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1287
        // 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...
1288
        // offset: 4; size: 2; OS version
1289
        // offset: 6; size: 2; OS indicator
1290
        // offset: 8; size: 16
1291
        // offset: 24; size: 4; section count
1292 4
        $secCount = self::getInt4d($this->summaryInformation, 24);
0 ignored issues
show
Unused Code introduced by
$secCount is not used, you could remove the assignment.

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

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

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

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

Loading history...
1293
1294
        // 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
1295
        // offset: 44; size: 4
1296 4
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1297
1298
        // section header
1299
        // offset: $secOffset; size: 4; section length
1300 4
        $secLength = self::getInt4d($this->summaryInformation, $secOffset);
0 ignored issues
show
Unused Code introduced by
$secLength is not used, you could remove the assignment.

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

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

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

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

Loading history...
1301
1302
        // offset: $secOffset+4; size: 4; property count
1303 4
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1304
1305
        // initialize code page (used to resolve string values)
1306 4
        $codePage = 'CP1252';
1307
1308
        // 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...
1309
        // loop through property decarations and properties
1310 4
        for ($i = 0; $i < $countProperties; ++$i) {
1311
            // 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...
1312 4
            $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
1313
1314
            // Use value of property id as appropriate
1315
            // 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...
1316 4
            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
1317
1318 4
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1319
1320
            // initialize property value
1321 4
            $value = null;
1322
1323
            // extract property value based on property type
1324
            switch ($type) {
1325 4
                case 0x02: // 2 byte signed integer
1326 4
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1327 4
                    break;
1328 4
                case 0x03: // 4 byte signed integer
1329 4
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1330 4
                    break;
1331 4
                case 0x13: // 4 byte unsigned integer
1332
                    // not needed yet, fix later if necessary
1333
                    break;
1334 4 View Code Duplication
                case 0x1E: // null-terminated string prepended by dword string length
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1335 4
                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1336 4
                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1337 4
                    $value = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1338 4
                    $value = rtrim($value);
1339 4
                    break;
1340 4 View Code Duplication
                case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1341
                    // PHP-time
1342 4
                    $value = \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1343 4
                    break;
1344
                case 0x47: // Clipboard format
1345
                    // not needed yet, fix later if necessary
1346
                    break;
1347
            }
1348
1349
            switch ($id) {
1350 4
                case 0x01:    //    Code Page
1351 4
                    $codePage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($value);
1352 4
                    break;
1353 4
                case 0x02:    //    Title
1354 3
                    $this->spreadsheet->getProperties()->setTitle($value);
1355 3
                    break;
1356 4
                case 0x03:    //    Subject
1357 3
                    $this->spreadsheet->getProperties()->setSubject($value);
1358 3
                    break;
1359 4
                case 0x04:    //    Author (Creator)
1360 4
                    $this->spreadsheet->getProperties()->setCreator($value);
1361 4
                    break;
1362 4
                case 0x05:    //    Keywords
1363 3
                    $this->spreadsheet->getProperties()->setKeywords($value);
1364 3
                    break;
1365 4
                case 0x06:    //    Comments (Description)
1366 3
                    $this->spreadsheet->getProperties()->setDescription($value);
1367 3
                    break;
1368 4
                case 0x07:    //    Template
1369
                    //    Not supported by PhpSpreadsheet
1370
                    break;
1371 4
                case 0x08:    //    Last Saved By (LastModifiedBy)
1372 4
                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
1373 4
                    break;
1374 4
                case 0x09:    //    Revision
1375
                    //    Not supported by PhpSpreadsheet
1376
                    break;
1377 4
                case 0x0A:    //    Total Editing Time
1378
                    //    Not supported by PhpSpreadsheet
1379
                    break;
1380 4
                case 0x0B:    //    Last Printed
1381
                    //    Not supported by PhpSpreadsheet
1382
                    break;
1383 4
                case 0x0C:    //    Created Date/Time
1384 4
                    $this->spreadsheet->getProperties()->setCreated($value);
1385 4
                    break;
1386 4
                case 0x0D:    //    Modified Date/Time
1387 4
                    $this->spreadsheet->getProperties()->setModified($value);
1388 4
                    break;
1389 4
                case 0x0E:    //    Number of Pages
1390
                    //    Not supported by PhpSpreadsheet
1391
                    break;
1392 4
                case 0x0F:    //    Number of Words
1393
                    //    Not supported by PhpSpreadsheet
1394
                    break;
1395 4
                case 0x10:    //    Number of Characters
1396
                    //    Not supported by PhpSpreadsheet
1397
                    break;
1398 4
                case 0x11:    //    Thumbnail
1399
                    //    Not supported by PhpSpreadsheet
1400
                    break;
1401 4
                case 0x12:    //    Name of creating application
1402
                    //    Not supported by PhpSpreadsheet
1403 2
                    break;
1404 4
                case 0x13:    //    Security
1405
                    //    Not supported by PhpSpreadsheet
1406 4
                    break;
1407
            }
1408
        }
1409 4
    }
1410
1411
    /**
1412
     * Read additional document summary information.
1413
     */
1414 4
    private function readDocumentSummaryInformation()
1415
    {
1416 4
        if (!isset($this->documentSummaryInformation)) {
1417
            return;
1418
        }
1419
1420
        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
1421
        //    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...
1422
        //    offset: 4;    size: 2;    OS version
1423
        //    offset: 6;    size: 2;    OS indicator
1424
        //    offset: 8;    size: 16
1425
        //    offset: 24;    size: 4;    section count
1426 4
        $secCount = self::getInt4d($this->documentSummaryInformation, 24);
0 ignored issues
show
Unused Code introduced by
$secCount is not used, you could remove the assignment.

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

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

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

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

Loading history...
1427
1428
        // 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
1429
        // offset: 44;    size: 4;    first section offset
1430 4
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1431
1432
        //    section header
1433
        //    offset: $secOffset;    size: 4;    section length
1434 4
        $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
0 ignored issues
show
Unused Code introduced by
$secLength is not used, you could remove the assignment.

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

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

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

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

Loading history...
1435
1436
        //    offset: $secOffset+4;    size: 4;    property count
1437 4
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1438
1439
        // initialize code page (used to resolve string values)
1440 4
        $codePage = 'CP1252';
1441
1442
        //    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...
1443
        //    loop through property decarations and properties
1444 4
        for ($i = 0; $i < $countProperties; ++$i) {
1445
            //    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...
1446 4
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
1447
1448
            // Use value of property id as appropriate
1449
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1450 4
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
1451
1452 4
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1453
1454
            // initialize property value
1455 4
            $value = null;
1456
1457
            // extract property value based on property type
1458
            switch ($type) {
1459 4
                case 0x02:    //    2 byte signed integer
1460 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1461 4
                    break;
1462 4
                case 0x03:    //    4 byte signed integer
1463 4
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1464 4
                    break;
1465 4
                case 0x0B:  // Boolean
1466 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1467 4
                    $value = ($value == 0 ? false : true);
1468 4
                    break;
1469 4
                case 0x13:    //    4 byte unsigned integer
1470
                    // not needed yet, fix later if necessary
1471
                    break;
1472 4 View Code Duplication
                case 0x1E:    //    null-terminated string prepended by dword string length
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1473 4
                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1474 4
                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1475 4
                    $value = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1476 4
                    $value = rtrim($value);
1477 4
                    break;
1478 4 View Code Duplication
                case 0x40:    //    Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1479
                    // PHP-Time
1480
                    $value = \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1481
                    break;
1482 4
                case 0x47:    //    Clipboard format
1483
                    // not needed yet, fix later if necessary
1484
                    break;
1485
            }
1486
1487
            switch ($id) {
1488 4
                case 0x01:    //    Code Page
1489 4
                    $codePage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($value);
1490 4
                    break;
1491 4
                case 0x02:    //    Category
1492 3
                    $this->spreadsheet->getProperties()->setCategory($value);
1493 3
                    break;
1494 4
                case 0x03:    //    Presentation Target
1495
                    //    Not supported by PhpSpreadsheet
1496
                    break;
1497 4
                case 0x04:    //    Bytes
1498
                    //    Not supported by PhpSpreadsheet
1499
                    break;
1500 4
                case 0x05:    //    Lines
1501
                    //    Not supported by PhpSpreadsheet
1502
                    break;
1503 4
                case 0x06:    //    Paragraphs
1504
                    //    Not supported by PhpSpreadsheet
1505
                    break;
1506 4
                case 0x07:    //    Slides
1507
                    //    Not supported by PhpSpreadsheet
1508
                    break;
1509 4
                case 0x08:    //    Notes
1510
                    //    Not supported by PhpSpreadsheet
1511
                    break;
1512 4
                case 0x09:    //    Hidden Slides
1513
                    //    Not supported by PhpSpreadsheet
1514
                    break;
1515 4
                case 0x0A:    //    MM Clips
1516
                    //    Not supported by PhpSpreadsheet
1517
                    break;
1518 4
                case 0x0B:    //    Scale Crop
1519
                    //    Not supported by PhpSpreadsheet
1520 4
                    break;
1521 4
                case 0x0C:    //    Heading Pairs
1522
                    //    Not supported by PhpSpreadsheet
1523 4
                    break;
1524 4
                case 0x0D:    //    Titles of Parts
1525
                    //    Not supported by PhpSpreadsheet
1526 4
                    break;
1527 4
                case 0x0E:    //    Manager
1528 1
                    $this->spreadsheet->getProperties()->setManager($value);
1529 1
                    break;
1530 4
                case 0x0F:    //    Company
1531 3
                    $this->spreadsheet->getProperties()->setCompany($value);
1532 3
                    break;
1533 4
                case 0x10:    //    Links up-to-date
1534
                    //    Not supported by PhpSpreadsheet
1535 4
                    break;
1536
            }
1537
        }
1538 4
    }
1539
1540
    /**
1541
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1542
     */
1543 4
    private function readDefault()
1544
    {
1545 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1546
1547
        // move stream pointer to next record
1548 4
        $this->pos += 4 + $length;
1549 4
    }
1550
1551
    /**
1552
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1553
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1554
     */
1555 1
    private function readNote()
1556
    {
1557 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1558 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1559
1560
        // move stream pointer to next record
1561 1
        $this->pos += 4 + $length;
1562
1563 1
        if ($this->readDataOnly) {
1564
            return;
1565
        }
1566
1567 1
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1568 1
        if ($this->version == self::XLS_BIFF8) {
1569 1
            $noteObjID = self::getInt2d($recordData, 6);
1570 1
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1571 1
            $noteAuthor = $noteAuthor['value'];
1572 1
            $this->cellNotes[$noteObjID] = [
1573 1
                'cellRef' => $cellAddress,
1574 1
                'objectID' => $noteObjID,
1575 1
                'author' => $noteAuthor,
1576
            ];
1577
        } else {
1578
            $extension = false;
1579
            if ($cellAddress == '$B$65536') {
1580
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1581
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1582
                //        max 2048 bytes will probably throw a wobbly.
1583
                $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...
1584
                $extension = true;
1585
                $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...
1586
            }
1587
1588
            $cellAddress = str_replace('$', '', $cellAddress);
1589
            $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...
1590
            $noteText = trim(substr($recordData, 6));
1591
1592
            if ($extension) {
1593
                //    Concatenate this extension with the currently set comment for the cell
1594
                $comment = $this->phpSheet->getComment($cellAddress);
1595
                $commentText = $comment->getText()->getPlainText();
1596
                $comment->setText($this->parseRichText($commentText . $noteText));
1597
            } else {
1598
                //    Set comment for the cell
1599
                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
1600
//                                                    ->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...
1601
            }
1602
        }
1603 1
    }
1604
1605
    /**
1606
     *    The TEXT Object record contains the text associated with a cell annotation.
1607
     */
1608 1
    private function readTextObject()
1609
    {
1610 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1611 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1612
1613
        // move stream pointer to next record
1614 1
        $this->pos += 4 + $length;
1615
1616 1
        if ($this->readDataOnly) {
1617
            return;
1618
        }
1619
1620
        // recordData consists of an array of subrecords looking like this:
1621
        //    grbit: 2 bytes; Option Flags
1622
        //    rot: 2 bytes; rotation
1623
        //    cchText: 2 bytes; length of the text (in the first continue record)
1624
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1625
        // followed by the continuation records containing the actual text and formatting
1626 1
        $grbitOpts = self::getInt2d($recordData, 0);
1627 1
        $rot = self::getInt2d($recordData, 2);
1628 1
        $cchText = self::getInt2d($recordData, 10);
1629 1
        $cbRuns = self::getInt2d($recordData, 12);
1630 1
        $text = $this->getSplicedRecordData();
1631
1632 1
        $this->textObjects[$this->textObjRef] = [
1633 1
            'text' => substr($text['recordData'], $text['spliceOffsets'][0] + 1, $cchText),
1634 1
            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
1635 1
            'alignment' => $grbitOpts,
1636 1
            'rotation' => $rot,
1637
        ];
1638 1
    }
1639
1640
    /**
1641
     * Read BOF.
1642
     */
1643 4
    private function readBof()
1644
    {
1645 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1646 4
        $recordData = substr($this->data, $this->pos + 4, $length);
1647
1648
        // move stream pointer to next record
1649 4
        $this->pos += 4 + $length;
1650
1651
        // offset: 2; size: 2; type of the following data
1652 4
        $substreamType = self::getInt2d($recordData, 2);
1653
1654
        switch ($substreamType) {
1655 4
            case self::XLS_WORKBOOKGLOBALS:
1656 4
                $version = self::getInt2d($recordData, 0);
1657 4
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1658
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1659
                }
1660 4
                $this->version = $version;
1661 4
                break;
1662 4
            case self::XLS_WORKSHEET:
1663
                // do not use this version information for anything
1664
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1665 4
                break;
1666
            default:
1667
                // substream, e.g. chart
1668
                // just skip the entire substream
1669
                do {
1670
                    $code = self::getInt2d($this->data, $this->pos);
1671
                    $this->readDefault();
1672
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1673
                break;
1674
        }
1675 4
    }
1676
1677
    /**
1678
     * FILEPASS.
1679
     *
1680
     * This record is part of the File Protection Block. It
1681
     * contains information about the read/write password of the
1682
     * file. All record contents following this record will be
1683
     * encrypted.
1684
     *
1685
     * --    "OpenOffice.org's Documentation of the Microsoft
1686
     *         Excel File Format"
1687
     *
1688
     * The decryption functions and objects used from here on in
1689
     * are based on the source of Spreadsheet-ParseExcel:
1690
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1691
     */
1692
    private function readFilepass()
1693
    {
1694
        $length = self::getInt2d($this->data, $this->pos + 2);
1695
1696
        if ($length != 54) {
1697
            throw new Exception('Unexpected file pass record length');
1698
        }
1699
1700
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1701
1702
        // move stream pointer to next record
1703
        $this->pos += 4 + $length;
1704
1705
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1706
            throw new Exception('Decryption password incorrect');
1707
        }
1708
1709
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1710
1711
        // Decryption required from the record after next onwards
1712
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1713
    }
1714
1715
    /**
1716
     * Make an RC4 decryptor for the given block.
1717
     *
1718
     * @param int         Block for which to create decrypto
1719
     * @param string $valContext MD5 context state
1720
     * @param mixed $block
1721
     *
1722
     * @return Xls\RC4
1723
     */
1724
    private function makeKey($block, $valContext)
1725
    {
1726
        $pwarray = str_repeat("\0", 64);
1727
1728
        for ($i = 0; $i < 5; ++$i) {
1729
            $pwarray[$i] = $valContext[$i];
1730
        }
1731
1732
        $pwarray[5] = chr($block & 0xff);
1733
        $pwarray[6] = chr(($block >> 8) & 0xff);
1734
        $pwarray[7] = chr(($block >> 16) & 0xff);
1735
        $pwarray[8] = chr(($block >> 24) & 0xff);
1736
1737
        $pwarray[9] = "\x80";
1738
        $pwarray[56] = "\x48";
1739
1740
        $md5 = new Xls\MD5();
1741
        $md5->add($pwarray);
1742
1743
        $s = $md5->getContext();
1744
1745
        return new Xls\RC4($s);
1746
    }
1747
1748
    /**
1749
     * Verify RC4 file password.
1750
     *
1751
     * @param string $password        Password to check
1752
     * @param string $docid           Document id
1753
     * @param string $salt_data       Salt data
1754
     * @param string $hashedsalt_data Hashed salt data
1755
     * @param string $valContext     Set to the MD5 context of the value
1756
     *
1757
     * @return bool Success
1758
     */
1759
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1760
    {
1761
        $pwarray = str_repeat("\0", 64);
1762
1763
        for ($i = 0; $i < strlen($password); ++$i) {
1764
            $o = ord(substr($password, $i, 1));
1765
            $pwarray[2 * $i] = chr($o & 0xff);
1766
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1767
        }
1768
        $pwarray[2 * $i] = chr(0x80);
1769
        $pwarray[56] = chr(($i << 4) & 0xff);
1770
1771
        $md5 = new Xls\MD5();
1772
        $md5->add($pwarray);
1773
1774
        $mdContext1 = $md5->getContext();
1775
1776
        $offset = 0;
1777
        $keyoffset = 0;
1778
        $tocopy = 5;
1779
1780
        $md5->reset();
1781
1782
        while ($offset != 16) {
1783
            if ((64 - $offset) < 5) {
1784
                $tocopy = 64 - $offset;
1785
            }
1786
            for ($i = 0; $i <= $tocopy; ++$i) {
1787
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1788
            }
1789
            $offset += $tocopy;
1790
1791
            if ($offset == 64) {
1792
                $md5->add($pwarray);
1793
                $keyoffset = $tocopy;
1794
                $tocopy = 5 - $tocopy;
1795
                $offset = 0;
1796
                continue;
1797
            }
1798
1799
            $keyoffset = 0;
1800
            $tocopy = 5;
1801
            for ($i = 0; $i < 16; ++$i) {
1802
                $pwarray[$offset + $i] = $docid[$i];
1803
            }
1804
            $offset += 16;
1805
        }
1806
1807
        $pwarray[16] = "\x80";
1808
        for ($i = 0; $i < 47; ++$i) {
1809
            $pwarray[17 + $i] = "\0";
1810
        }
1811
        $pwarray[56] = "\x80";
1812
        $pwarray[57] = "\x0a";
1813
1814
        $md5->add($pwarray);
1815
        $valContext = $md5->getContext();
1816
1817
        $key = $this->makeKey(0, $valContext);
1818
1819
        $salt = $key->RC4($salt_data);
1820
        $hashedsalt = $key->RC4($hashedsalt_data);
1821
1822
        $salt .= "\x80" . str_repeat("\0", 47);
1823
        $salt[56] = "\x80";
1824
1825
        $md5->reset();
1826
        $md5->add($salt);
1827
        $mdContext2 = $md5->getContext();
1828
1829
        return $mdContext2 == $hashedsalt;
1830
    }
1831
1832
    /**
1833
     * CODEPAGE.
1834
     *
1835
     * This record stores the text encoding used to write byte
1836
     * strings, stored as MS Windows code page identifier.
1837
     *
1838
     * --    "OpenOffice.org's Documentation of the Microsoft
1839
     *         Excel File Format"
1840
     */
1841 4 View Code Duplication
    private function readCodepage()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1842
    {
1843 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1844 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1845
1846
        // move stream pointer to next record
1847 4
        $this->pos += 4 + $length;
1848
1849
        // offset: 0; size: 2; code page identifier
1850 4
        $codepage = self::getInt2d($recordData, 0);
1851
1852 4
        $this->codepage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($codepage);
1853 4
    }
1854
1855
    /**
1856
     * DATEMODE.
1857
     *
1858
     * This record specifies the base date for displaying date
1859
     * values. All dates are stored as count of days past this
1860
     * base date. In BIFF2-BIFF4 this record is part of the
1861
     * Calculation Settings Block. In BIFF5-BIFF8 it is
1862
     * stored in the Workbook Globals Substream.
1863
     *
1864
     * --    "OpenOffice.org's Documentation of the Microsoft
1865
     *         Excel File Format"
1866
     */
1867 4
    private function readDateMode()
1868
    {
1869 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1870 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1871
1872
        // move stream pointer to next record
1873 4
        $this->pos += 4 + $length;
1874
1875
        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
1876 4
        \PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900);
1877 4
        if (ord($recordData[0]) == 1) {
1878
            \PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR_MAC_1904);
1879
        }
1880 4
    }
1881
1882
    /**
1883
     * Read a FONT record.
1884
     */
1885 4
    private function readFont()
1886
    {
1887 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1888 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1889
1890
        // move stream pointer to next record
1891 4
        $this->pos += 4 + $length;
1892
1893 4
        if (!$this->readDataOnly) {
1894 4
            $objFont = new \PhpOffice\PhpSpreadsheet\Style\Font();
1895
1896
            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
1897 4
            $size = self::getInt2d($recordData, 0);
1898 4
            $objFont->setSize($size / 20);
1899
1900
            // offset: 2; size: 2; option flags
1901
            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
1902
            // bit: 1; mask 0x0002; italic
1903 4
            $isItalic = (0x0002 & self::getInt2d($recordData, 2)) >> 1;
1904 4
            if ($isItalic) {
1905 4
                $objFont->setItalic(true);
1906
            }
1907
1908
            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
1909
            // bit: 3; mask 0x0008; strike
1910 4
            $isStrike = (0x0008 & self::getInt2d($recordData, 2)) >> 3;
1911 4
            if ($isStrike) {
1912
                $objFont->setStrikethrough(true);
1913
            }
1914
1915
            // offset: 4; size: 2; colour index
1916 4
            $colorIndex = self::getInt2d($recordData, 4);
1917 4
            $objFont->colorIndex = $colorIndex;
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpOffice\PhpSpreadsheet\Style\Font.

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

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

Loading history...
1918
1919
            // offset: 6; size: 2; font weight
1920 4
            $weight = self::getInt2d($recordData, 6);
1921
            switch ($weight) {
1922 4
                case 0x02BC:
1923 4
                    $objFont->setBold(true);
1924 4
                    break;
1925
            }
1926
1927
            // offset: 8; size: 2; escapement type
1928 4
            $escapement = self::getInt2d($recordData, 8);
1929
            switch ($escapement) {
1930 4
                case 0x0001:
1931
                    $objFont->setSuperScript(true);
1932
                    break;
1933 4
                case 0x0002:
1934
                    $objFont->setSubScript(true);
1935
                    break;
1936
            }
1937
1938
            // offset: 10; size: 1; underline type
1939 4
            $underlineType = ord($recordData[10]);
1940
            switch ($underlineType) {
1941 4
                case 0x00:
1942 4
                    break; // no underline
1943 2
                case 0x01:
1944 2
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1945 2
                    break;
1946
                case 0x02:
1947
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE);
1948
                    break;
1949
                case 0x21:
1950
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING);
1951
                    break;
1952
                case 0x22:
1953
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING);
1954
                    break;
1955
            }
1956
1957
            // offset: 11; size: 1; font family
1958
            // offset: 12; size: 1; character set
1959
            // offset: 13; size: 1; not used
1960
            // offset: 14; size: var; font name
1961 4 View Code Duplication
            if ($this->version == self::XLS_BIFF8) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1962 4
                $string = self::readUnicodeStringShort(substr($recordData, 14));
1963
            } else {
1964
                $string = $this->readByteStringShort(substr($recordData, 14));
1965
            }
1966 4
            $objFont->setName($string['value']);
1967
1968 4
            $this->objFonts[] = $objFont;
1969
        }
1970 4
    }
1971
1972
    /**
1973
     * FORMAT.
1974
     *
1975
     * This record contains information about a number format.
1976
     * All FORMAT records occur together in a sequential list.
1977
     *
1978
     * In BIFF2-BIFF4 other records referencing a FORMAT record
1979
     * contain a zero-based index into this list. From BIFF5 on
1980
     * the FORMAT record contains the index itself that will be
1981
     * used by other records.
1982
     *
1983
     * --    "OpenOffice.org's Documentation of the Microsoft
1984
     *         Excel File Format"
1985
     */
1986 4
    private function readFormat()
1987
    {
1988 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1989 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1990
1991
        // move stream pointer to next record
1992 4
        $this->pos += 4 + $length;
1993
1994 4
        if (!$this->readDataOnly) {
1995 4
            $indexCode = self::getInt2d($recordData, 0);
1996
1997 4 View Code Duplication
            if ($this->version == self::XLS_BIFF8) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1998 4
                $string = self::readUnicodeStringLong(substr($recordData, 2));
1999
            } else {
2000
                // BIFF7
2001
                $string = $this->readByteStringShort(substr($recordData, 2));
2002
            }
2003
2004 4
            $formatString = $string['value'];
2005 4
            $this->formats[$indexCode] = $formatString;
2006
        }
2007 4
    }
2008
2009
    /**
2010
     * XF - Extended Format.
2011
     *
2012
     * This record contains formatting information for cells, rows, columns or styles.
2013
     * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
2014
     * and 1 cell XF.
2015
     * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
2016
     * and XF record 15 is a cell XF
2017
     * We only read the first cell style XF and skip the remaining cell style XF records
2018
     * We read all cell XF records.
2019
     *
2020
     * --    "OpenOffice.org's Documentation of the Microsoft
2021
     *         Excel File Format"
2022
     */
2023 4
    private function readXf()
2024
    {
2025 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2026 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2027
2028
        // move stream pointer to next record
2029 4
        $this->pos += 4 + $length;
2030
2031 4
        $objStyle = new \PhpOffice\PhpSpreadsheet\Style();
2032
2033 4
        if (!$this->readDataOnly) {
2034
            // offset:  0; size: 2; Index to FONT record
2035 4
            if (self::getInt2d($recordData, 0) < 4) {
2036 4
                $fontIndex = self::getInt2d($recordData, 0);
2037
            } else {
2038
                // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
2039
                // check the OpenOffice documentation of the FONT record
2040 4
                $fontIndex = self::getInt2d($recordData, 0) - 1;
2041
            }
2042 4
            $objStyle->setFont($this->objFonts[$fontIndex]);
2043
2044
            // offset:  2; size: 2; Index to FORMAT record
2045 4
            $numberFormatIndex = self::getInt2d($recordData, 2);
2046 4
            if (isset($this->formats[$numberFormatIndex])) {
2047
                // then we have user-defined format code
2048 3
                $numberformat = ['code' => $this->formats[$numberFormatIndex]];
2049 4
            } elseif (($code = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
2050
                // then we have built-in format code
2051 4
                $numberformat = ['code' => $code];
2052
            } else {
2053
                // we set the general format code
2054
                $numberformat = ['code' => 'General'];
2055
            }
2056 4
            $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
2057
2058
            // offset:  4; size: 2; XF type, cell protection, and parent style XF
2059
            // 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...
2060 4
            $xfTypeProt = self::getInt2d($recordData, 4);
2061
            // bit 0; mask 0x01; 1 = cell is locked
2062 4
            $isLocked = (0x01 & $xfTypeProt) >> 0;
2063 4
            $objStyle->getProtection()->setLocked($isLocked ? \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_INHERIT : \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2064
2065
            // bit 1; mask 0x02; 1 = Formula is hidden
2066 4
            $isHidden = (0x02 & $xfTypeProt) >> 1;
2067 4
            $objStyle->getProtection()->setHidden($isHidden ? \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_PROTECTED : \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2068
2069
            // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
2070 4
            $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
2071
2072
            // offset:  6; size: 1; Alignment and text break
2073
            // bit 2-0, mask 0x07; horizontal alignment
2074 4
            $horAlign = (0x07 & ord($recordData[6])) >> 0;
2075
            switch ($horAlign) {
2076 4
                case 0:
2077 4
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL);
2078 4
                    break;
2079 2
                case 1:
2080 2
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
2081 2
                    break;
2082 2
                case 2:
2083
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
2084
                    break;
2085 2
                case 3:
2086 2
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT);
2087 2
                    break;
2088 2
                case 4:
2089
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_FILL);
2090
                    break;
2091 2
                case 5:
2092 2
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY);
2093 2
                    break;
2094
                case 6:
2095
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS);
2096
                    break;
2097
            }
2098
            // bit 3, mask 0x08; wrap text
2099 4
            $wrapText = (0x08 & ord($recordData[6])) >> 3;
2100
            switch ($wrapText) {
2101 4
                case 0:
2102 4
                    $objStyle->getAlignment()->setWrapText(false);
2103 4
                    break;
2104 2
                case 1:
2105 2
                    $objStyle->getAlignment()->setWrapText(true);
2106 2
                    break;
2107
            }
2108
            // bit 6-4, mask 0x70; vertical alignment
2109 4
            $vertAlign = (0x70 & ord($recordData[6])) >> 4;
2110
            switch ($vertAlign) {
2111 4
                case 0:
2112
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP);
2113
                    break;
2114 4
                case 1:
2115 2
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
2116 2
                    break;
2117 4
                case 2:
2118 4
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM);
2119 4
                    break;
2120
                case 3:
2121
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_JUSTIFY);
2122
                    break;
2123
            }
2124
2125 4
            if ($this->version == self::XLS_BIFF8) {
2126
                // offset:  7; size: 1; XF_ROTATION: Text rotation angle
2127 4
                $angle = ord($recordData[7]);
2128 4
                $rotation = 0;
2129 4
                if ($angle <= 90) {
2130 4
                    $rotation = $angle;
2131
                } elseif ($angle <= 180) {
2132
                    $rotation = 90 - $angle;
2133
                } elseif ($angle == 255) {
2134
                    $rotation = -165;
2135
                }
2136 4
                $objStyle->getAlignment()->setTextRotation($rotation);
2137
2138
                // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
2139
                // bit: 3-0; mask: 0x0F; indent level
2140 4
                $indent = (0x0F & ord($recordData[8])) >> 0;
2141 4
                $objStyle->getAlignment()->setIndent($indent);
2142
2143
                // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
2144 4
                $shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
2145
                switch ($shrinkToFit) {
2146 4
                    case 0:
2147 4
                        $objStyle->getAlignment()->setShrinkToFit(false);
2148 4
                        break;
2149 1
                    case 1:
2150 1
                        $objStyle->getAlignment()->setShrinkToFit(true);
2151 1
                        break;
2152
                }
2153
2154
                // offset:  9; size: 1; Flags used for attribute groups
2155
2156
                // offset: 10; size: 4; Cell border lines and background area
2157
                // bit: 3-0; mask: 0x0000000F; left style
2158 4 View Code Duplication
                if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2159 4
                    $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
2160
                }
2161
                // bit: 7-4; mask: 0x000000F0; right style
2162 4 View Code Duplication
                if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2163 4
                    $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
2164
                }
2165
                // bit: 11-8; mask: 0x00000F00; top style
2166 4 View Code Duplication
                if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2167 4
                    $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
2168
                }
2169
                // bit: 15-12; mask: 0x0000F000; bottom style
2170 4 View Code Duplication
                if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2171 4
                    $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
2172
                }
2173
                // bit: 22-16; mask: 0x007F0000; left color
2174 4
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpOffice\PhpSpreadsheet\Style\Border.

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

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

Loading history...
2175
2176
                // bit: 29-23; mask: 0x3F800000; right color
2177 4
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2178
2179
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2180 4
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2181
2182
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2183 4
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2184
2185 4
                if ($diagonalUp == false && $diagonalDown == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
2186 4
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_NONE);
2187
                } 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...
2188
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_UP);
2189
                } 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...
2190
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_DOWN);
2191
                } 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...
2192
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_BOTH);
2193
                }
2194
2195
                // 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...
2196
                // bit: 6-0; mask: 0x0000007F; top color
2197 4
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2198
2199
                // bit: 13-7; mask: 0x00003F80; bottom color
2200 4
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2201
2202
                // bit: 20-14; mask: 0x001FC000; diagonal color
2203 4
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2204
2205
                // bit: 24-21; mask: 0x01E00000; diagonal style
2206 4 View Code Duplication
                if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2207 4
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2208
                }
2209
2210
                // bit: 31-26; mask: 0xFC000000 fill pattern
2211 4
                if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2212 4
                    $objStyle->getFill()->setFillType($fillType);
2213
                }
2214
                // offset: 18; size: 2; pattern and background colour
2215
                // bit: 6-0; mask: 0x007F; color index for pattern color
2216 4
                $objStyle->getFill()->startcolorIndex = (0x007F & self::getInt2d($recordData, 18)) >> 0;
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

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

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

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

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

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

Loading history...
2220
            } else {
2221
                // BIFF5
2222
2223
                // offset: 7; size: 1; Text orientation and flags
2224
                $orientationAndFlags = ord($recordData[7]);
2225
2226
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2227
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2228
                switch ($xfOrientation) {
2229
                    case 0:
2230
                        $objStyle->getAlignment()->setTextRotation(0);
2231
                        break;
2232
                    case 1:
2233
                        $objStyle->getAlignment()->setTextRotation(-165);
2234
                        break;
2235
                    case 2:
2236
                        $objStyle->getAlignment()->setTextRotation(90);
2237
                        break;
2238
                    case 3:
2239
                        $objStyle->getAlignment()->setTextRotation(-90);
2240
                        break;
2241
                }
2242
2243
                // offset: 8; size: 4; cell border lines and background area
2244
                $borderAndBackground = self::getInt4d($recordData, 8);
2245
2246
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2247
                $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...
2248
2249
                // bit: 13-7; mask: 0x00003F80; color index for pattern background
2250
                $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...
2251
2252
                // bit: 21-16; mask: 0x003F0000; fill pattern
2253
                $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2254
2255
                // bit: 24-22; mask: 0x01C00000; bottom line style
2256
                $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2257
2258
                // bit: 31-25; mask: 0xFE000000; bottom line color
2259
                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2260
2261
                // offset: 12; size: 4; cell border lines
2262
                $borderLines = self::getInt4d($recordData, 12);
2263
2264
                // bit: 2-0; mask: 0x00000007; top line style
2265
                $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2266
2267
                // bit: 5-3; mask: 0x00000038; left line style
2268
                $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2269
2270
                // bit: 8-6; mask: 0x000001C0; right line style
2271
                $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2272
2273
                // bit: 15-9; mask: 0x0000FE00; top line color index
2274
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2275
2276
                // bit: 22-16; mask: 0x007F0000; left line color index
2277
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2278
2279
                // bit: 29-23; mask: 0x3F800000; right line color index
2280
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2281
            }
2282
2283
            // add cellStyleXf or cellXf and update mapping
2284 4
            if ($isCellStyleXf) {
2285
                // we only read one style XF record which is always the first
2286 4
                if ($this->xfIndex == 0) {
2287 4
                    $this->spreadsheet->addCellStyleXf($objStyle);
2288 4
                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
2289
                }
2290
            } else {
2291
                // we read all cell XF records
2292 4
                $this->spreadsheet->addCellXf($objStyle);
2293 4
                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2294
            }
2295
2296
            // update XF index for when we read next record
2297 4
            ++$this->xfIndex;
2298
        }
2299 4
    }
2300
2301 3
    private function readXfExt()
2302
    {
2303 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2304 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2305
2306
        // move stream pointer to next record
2307 3
        $this->pos += 4 + $length;
2308
2309 3
        if (!$this->readDataOnly) {
2310
            // offset: 0; size: 2; 0x087D = repeated header
2311
2312
            // offset: 2; size: 2
2313
2314
            // offset: 4; size: 8; not used
2315
2316
            // offset: 12; size: 2; record version
2317
2318
            // offset: 14; size: 2; index to XF record which this record modifies
2319 3
            $ixfe = self::getInt2d($recordData, 14);
2320
2321
            // offset: 16; size: 2; not used
2322
2323
            // offset: 18; size: 2; number of extension properties that follow
2324 3
            $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...
2325
2326
            // start reading the actual extension data
2327 3
            $offset = 20;
2328 3
            while ($offset < $length) {
2329
                // extension type
2330 3
                $extType = self::getInt2d($recordData, $offset);
2331
2332
                // extension length
2333 3
                $cb = self::getInt2d($recordData, $offset + 2);
2334
2335
                // extension data
2336 3
                $extData = substr($recordData, $offset + 4, $cb);
2337
2338
                switch ($extType) {
2339 3 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...
2340 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2341 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2342
2343 3
                        if ($xclfType == 2) {
2344 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2345
2346
                            // modify the relevant style property
2347 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2348 1
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2349 1
                                $fill->getStartColor()->setRGB($rgb);
2350 1
                                unset($fill->startcolorIndex); // normal color index does not apply, discard
2351
                            }
2352
                        }
2353 3
                        break;
2354 3 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...
2355 1
                        $xclfType = self::getInt2d($extData, 0); // color type
2356 1
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2357
2358 1
                        if ($xclfType == 2) {
2359 1
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2360
2361
                            // modify the relevant style property
2362 1
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2363 1
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2364 1
                                $fill->getEndColor()->setRGB($rgb);
2365 1
                                unset($fill->endcolorIndex); // normal color index does not apply, discard
2366
                            }
2367
                        }
2368 1
                        break;
2369 3 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...
2370 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2371 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2372
2373 3
                        if ($xclfType == 2) {
2374 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2375
2376
                            // modify the relevant style property
2377 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2378 1
                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2379 1
                                $top->getColor()->setRGB($rgb);
2380 1
                                unset($top->colorIndex); // normal color index does not apply, discard
2381
                            }
2382
                        }
2383 3
                        break;
2384 3 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...
2385 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2386 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2387
2388 3
                        if ($xclfType == 2) {
2389 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2390
2391
                            // modify the relevant style property
2392 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2393 1
                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2394 1
                                $bottom->getColor()->setRGB($rgb);
2395 1
                                unset($bottom->colorIndex); // normal color index does not apply, discard
2396
                            }
2397
                        }
2398 3
                        break;
2399 3 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...
2400 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2401 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2402
2403 3
                        if ($xclfType == 2) {
2404 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2405
2406
                            // modify the relevant style property
2407 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2408 1
                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2409 1
                                $left->getColor()->setRGB($rgb);
2410 1
                                unset($left->colorIndex); // normal color index does not apply, discard
2411
                            }
2412
                        }
2413 3
                        break;
2414 3 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...
2415 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2416 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2417
2418 3
                        if ($xclfType == 2) {
2419 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2420
2421
                            // modify the relevant style property
2422 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2423 1
                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2424 1
                                $right->getColor()->setRGB($rgb);
2425 1
                                unset($right->colorIndex); // normal color index does not apply, discard
2426
                            }
2427
                        }
2428 3
                        break;
2429 3 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...
2430
                        $xclfType = self::getInt2d($extData, 0); // color type
2431
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2432
2433
                        if ($xclfType == 2) {
2434
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2435
2436
                            // modify the relevant style property
2437
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2438
                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2439
                                $diagonal->getColor()->setRGB($rgb);
2440
                                unset($diagonal->colorIndex); // normal color index does not apply, discard
2441
                            }
2442
                        }
2443
                        break;
2444 3 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...
2445 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2446 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2447
2448 3
                        if ($xclfType == 2) {
2449 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2450
2451
                            // modify the relevant style property
2452 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2453 1
                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2454 1
                                $font->getColor()->setRGB($rgb);
2455 1
                                unset($font->colorIndex); // normal color index does not apply, discard
2456
                            }
2457
                        }
2458 3
                        break;
2459
                }
2460
2461 3
                $offset += $cb;
2462
            }
2463
        }
2464 3
    }
2465
2466
    /**
2467
     * Read STYLE record.
2468
     */
2469 4
    private function readStyle()
2470
    {
2471 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2472 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2473
2474
        // move stream pointer to next record
2475 4
        $this->pos += 4 + $length;
2476
2477 4
        if (!$this->readDataOnly) {
2478
            // offset: 0; size: 2; index to XF record and flag for built-in style
2479 4
            $ixfe = self::getInt2d($recordData, 0);
2480
2481
            // bit: 11-0; mask 0x0FFF; index to XF record
2482 4
            $xfIndex = (0x0FFF & $ixfe) >> 0;
0 ignored issues
show
Unused Code introduced by
$xfIndex is not used, you could remove the assignment.

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

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

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

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

Loading history...
2483
2484
            // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
2485 4
            $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
2486
2487 4
            if ($isBuiltIn) {
2488
                // offset: 2; size: 1; identifier for built-in style
2489 4
                $builtInId = ord($recordData[2]);
2490
2491
                switch ($builtInId) {
2492 4
                    case 0x00:
2493
                        // currently, we are not using this for anything
2494 4
                        break;
2495
                    default:
2496 2
                        break;
2497
                }
2498
            }
2499
                // user-defined; not supported by PhpSpreadsheet
2500
        }
2501 4
    }
2502
2503
    /**
2504
     * Read PALETTE record.
2505
     */
2506 2
    private function readPalette()
2507
    {
2508 2
        $length = self::getInt2d($this->data, $this->pos + 2);
2509 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2510
2511
        // move stream pointer to next record
2512 2
        $this->pos += 4 + $length;
2513
2514 2
        if (!$this->readDataOnly) {
2515
            // offset: 0; size: 2; number of following colors
2516 2
            $nm = self::getInt2d($recordData, 0);
2517
2518
            // list of RGB colors
2519 2
            for ($i = 0; $i < $nm; ++$i) {
2520 2
                $rgb = substr($recordData, 2 + 4 * $i, 4);
2521 2
                $this->palette[] = self::readRGB($rgb);
2522
            }
2523
        }
2524 2
    }
2525
2526
    /**
2527
     * SHEET.
2528
     *
2529
     * This record is  located in the  Workbook Globals
2530
     * Substream  and represents a sheet inside the workbook.
2531
     * One SHEET record is written for each sheet. It stores the
2532
     * sheet name and a stream offset to the BOF record of the
2533
     * respective Sheet Substream within the Workbook Stream.
2534
     *
2535
     * --    "OpenOffice.org's Documentation of the Microsoft
2536
     *         Excel File Format"
2537
     */
2538 4
    private function readSheet()
2539
    {
2540 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2541 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2542
2543
        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
2544
        // NOTE: not encrypted
2545 4
        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
2546
2547
        // move stream pointer to next record
2548 4
        $this->pos += 4 + $length;
2549
2550
        // offset: 4; size: 1; sheet state
2551 4
        switch (ord($recordData[4])) {
2552 4
            case 0x00:
2553 4
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2554 4
                break;
2555
            case 0x01:
2556
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_HIDDEN;
2557
                break;
2558
            case 0x02:
2559
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_VERYHIDDEN;
2560
                break;
2561
            default:
2562
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2563
                break;
2564
        }
2565
2566
        // offset: 5; size: 1; sheet type
2567 4
        $sheetType = ord($recordData[5]);
2568
2569
        // offset: 6; size: var; sheet name
2570 4 View Code Duplication
        if ($this->version == self::XLS_BIFF8) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2571 4
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2572 4
            $rec_name = $string['value'];
2573
        } elseif ($this->version == self::XLS_BIFF7) {
2574
            $string = $this->readByteStringShort(substr($recordData, 6));
2575
            $rec_name = $string['value'];
2576
        }
2577
2578 4
        $this->sheets[] = [
2579 4
            'name' => $rec_name,
0 ignored issues
show
Bug introduced by
The variable $rec_name does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2580 4
            'offset' => $rec_offset,
2581 4
            'sheetState' => $sheetState,
2582 4
            'sheetType' => $sheetType,
2583
        ];
2584 4
    }
2585
2586
    /**
2587
     * Read EXTERNALBOOK record.
2588
     */
2589 3
    private function readExternalBook()
2590
    {
2591 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2592 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2593
2594
        // move stream pointer to next record
2595 3
        $this->pos += 4 + $length;
2596
2597
        // offset within record data
2598 3
        $offset = 0;
2599
2600
        // there are 4 types of records
2601 3
        if (strlen($recordData) > 4) {
2602
            // external reference
2603
            // offset: 0; size: 2; number of sheet names ($nm)
2604
            $nm = self::getInt2d($recordData, 0);
2605
            $offset += 2;
2606
2607
            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
2608
            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2609
            $offset += $encodedUrlString['size'];
2610
2611
            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
2612
            $externalSheetNames = [];
2613
            for ($i = 0; $i < $nm; ++$i) {
2614
                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2615
                $externalSheetNames[] = $externalSheetNameString['value'];
2616
                $offset += $externalSheetNameString['size'];
2617
            }
2618
2619
            // store the record data
2620
            $this->externalBooks[] = [
2621
                'type' => 'external',
2622
                'encodedUrl' => $encodedUrlString['value'],
2623
                'externalSheetNames' => $externalSheetNames,
2624
            ];
2625 3
        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
2626
            // internal reference
2627
            // offset: 0; size: 2; number of sheet in this document
2628
            // 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...
2629 3
            $this->externalBooks[] = [
2630 3
                'type' => 'internal',
2631
            ];
2632
        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
2633
            // add-in function
2634
            // 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...
2635
            $this->externalBooks[] = [
2636
                'type' => 'addInFunction',
2637
            ];
2638
        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
2639
            // DDE links, OLE links
2640
            // 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...
2641
            // offset: 2; size: var; encoded source document name
2642
            $this->externalBooks[] = [
2643
                'type' => 'DDEorOLE',
2644
            ];
2645
        }
2646 3
    }
2647
2648
    /**
2649
     * Read EXTERNNAME record.
2650
     */
2651
    private function readExternName()
2652
    {
2653
        $length = self::getInt2d($this->data, $this->pos + 2);
2654
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2655
2656
        // move stream pointer to next record
2657
        $this->pos += 4 + $length;
2658
2659
        // external sheet references provided for named cells
2660
        if ($this->version == self::XLS_BIFF8) {
2661
            // offset: 0; size: 2; options
2662
            $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...
2663
2664
            // 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...
2665
2666
            // offset: 4; size: 2; not used
2667
2668
            // offset: 6; size: var
2669
            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2670
2671
            // offset: var; size: var; formula data
2672
            $offset = 6 + $nameString['size'];
2673
            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
2674
2675
            $this->externalNames[] = [
2676
                'name' => $nameString['value'],
2677
                'formula' => $formula,
2678
            ];
2679
        }
2680
    }
2681
2682
    /**
2683
     * Read EXTERNSHEET record.
2684
     */
2685 3
    private function readExternSheet()
2686
    {
2687 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2688 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2689
2690
        // move stream pointer to next record
2691 3
        $this->pos += 4 + $length;
2692
2693
        // external sheet references provided for named cells
2694 3
        if ($this->version == self::XLS_BIFF8) {
2695
            // offset: 0; size: 2; number of following ref structures
2696 3
            $nm = self::getInt2d($recordData, 0);
2697 3
            for ($i = 0; $i < $nm; ++$i) {
2698 3
                $this->ref[] = [
2699
                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
2700 3
                    'externalBookIndex' => self::getInt2d($recordData, 2 + 6 * $i),
2701
                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
2702 3
                    'firstSheetIndex' => self::getInt2d($recordData, 4 + 6 * $i),
2703
                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
2704 3
                    'lastSheetIndex' => self::getInt2d($recordData, 6 + 6 * $i),
2705
                ];
2706
            }
2707
        }
2708 3
    }
2709
2710
    /**
2711
     * DEFINEDNAME.
2712
     *
2713
     * This record is part of a Link Table. It contains the name
2714
     * and the token array of an internal defined name. Token
2715
     * arrays of defined names contain tokens with aberrant
2716
     * token classes.
2717
     *
2718
     * --    "OpenOffice.org's Documentation of the Microsoft
2719
     *         Excel File Format"
2720
     */
2721 1
    private function readDefinedName()
2722
    {
2723 1
        $length = self::getInt2d($this->data, $this->pos + 2);
2724 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2725
2726
        // move stream pointer to next record
2727 1
        $this->pos += 4 + $length;
2728
2729 1
        if ($this->version == self::XLS_BIFF8) {
2730
            // retrieves named cells
2731
2732
            // offset: 0; size: 2; option flags
2733 1
            $opts = self::getInt2d($recordData, 0);
2734
2735
            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
2736 1
            $isBuiltInName = (0x0020 & $opts) >> 5;
2737
2738
            // offset: 2; size: 1; keyboard shortcut
2739
2740
            // offset: 3; size: 1; length of the name (character count)
2741 1
            $nlen = ord($recordData[3]);
2742
2743
            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
2744
            // note: there can also be additional data, this is not included in $flen
2745 1
            $flen = self::getInt2d($recordData, 4);
2746
2747
            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
2748 1
            $scope = self::getInt2d($recordData, 8);
2749
2750
            // offset: 14; size: var; Name (Unicode string without length field)
2751 1
            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2752
2753
            // offset: var; size: $flen; formula data
2754 1
            $offset = 14 + $string['size'];
2755 1
            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
2756
2757
            try {
2758 1
                $formula = $this->getFormulaFromStructure($formulaStructure);
2759
            } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
2760
                $formula = '';
2761
            }
2762
2763 1
            $this->definedname[] = [
2764 1
                'isBuiltInName' => $isBuiltInName,
2765 1
                'name' => $string['value'],
2766 1
                'formula' => $formula,
2767 1
                'scope' => $scope,
2768
            ];
2769
        }
2770 1
    }
2771
2772
    /**
2773
     * Read MSODRAWINGGROUP record.
2774
     */
2775 3 View Code Duplication
    private function readMsoDrawingGroup()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2776
    {
2777 3
        $length = self::getInt2d($this->data, $this->pos + 2);
0 ignored issues
show
Unused Code introduced by
$length is not used, you could remove the assignment.

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

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

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

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

Loading history...
2778
2779
        // get spliced record data
2780 3
        $splicedRecordData = $this->getSplicedRecordData();
2781 3
        $recordData = $splicedRecordData['recordData'];
2782
2783 3
        $this->drawingGroupData .= $recordData;
2784 3
    }
2785
2786
    /**
2787
     * SST - Shared String Table.
2788
     *
2789
     * This record contains a list of all strings used anywhere
2790
     * in the workbook. Each string occurs only once. The
2791
     * workbook uses indexes into the list to reference the
2792
     * strings.
2793
     *
2794
     * --    "OpenOffice.org's Documentation of the Microsoft
2795
     *         Excel File Format"
2796
     **/
2797 4
    private function readSst()
2798
    {
2799
        // offset within (spliced) record data
2800 4
        $pos = 0;
2801
2802
        // get spliced record data
2803 4
        $splicedRecordData = $this->getSplicedRecordData();
2804
2805 4
        $recordData = $splicedRecordData['recordData'];
2806 4
        $spliceOffsets = $splicedRecordData['spliceOffsets'];
2807
2808
        // offset: 0; size: 4; total number of strings in the workbook
2809 4
        $pos += 4;
2810
2811
        // offset: 4; size: 4; number of following strings ($nm)
2812 4
        $nm = self::getInt4d($recordData, 4);
2813 4
        $pos += 4;
2814
2815
        // loop through the Unicode strings (16-bit length)
2816 4
        for ($i = 0; $i < $nm; ++$i) {
2817
            // number of characters in the Unicode string
2818 4
            $numChars = self::getInt2d($recordData, $pos);
2819 4
            $pos += 2;
2820
2821
            // option flags
2822 4
            $optionFlags = ord($recordData[$pos]);
2823 4
            ++$pos;
2824
2825
            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
2826 4
            $isCompressed = (($optionFlags & 0x01) == 0);
2827
2828
            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
2829 4
            $hasAsian = (($optionFlags & 0x04) != 0);
2830
2831
            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
2832 4
            $hasRichText = (($optionFlags & 0x08) != 0);
2833
2834 4
            if ($hasRichText) {
2835
                // number of Rich-Text formatting runs
2836 2
                $formattingRuns = self::getInt2d($recordData, $pos);
2837 2
                $pos += 2;
2838
            }
2839
2840 4
            if ($hasAsian) {
2841
                // size of Asian phonetic setting
2842
                $extendedRunLength = self::getInt4d($recordData, $pos);
2843
                $pos += 4;
2844
            }
2845
2846
            // expected byte length of character array if not split
2847 4
            $len = ($isCompressed) ? $numChars : $numChars * 2;
2848
2849
            // look up limit position
2850 4
            foreach ($spliceOffsets as $spliceOffset) {
2851
                // it can happen that the string is empty, therefore we need
2852
                // <= and not just <
2853 4
                if ($pos <= $spliceOffset) {
2854 4
                    $limitpos = $spliceOffset;
2855 4
                    break;
2856
                }
2857
            }
2858
2859 4
            if ($pos + $len <= $limitpos) {
2860
                // character array is not split between records
2861
2862 4
                $retstr = substr($recordData, $pos, $len);
2863 4
                $pos += $len;
2864
            } else {
2865
                // character array is split between records
2866
2867
                // first part of character array
2868
                $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...
2869
2870
                $bytesRead = $limitpos - $pos;
2871
2872
                // remaining characters in Unicode string
2873
                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2874
2875
                $pos = $limitpos;
2876
2877
                // keep reading the characters
2878
                while ($charsLeft > 0) {
2879
                    // look up next limit position, in case the string span more than one continue record
2880
                    foreach ($spliceOffsets as $spliceOffset) {
2881
                        if ($pos < $spliceOffset) {
2882
                            $limitpos = $spliceOffset;
2883
                            break;
2884
                        }
2885
                    }
2886
2887
                    // repeated option flags
2888
                    // OpenOffice.org documentation 5.21
2889
                    $option = ord($recordData[$pos]);
2890
                    ++$pos;
2891
2892
                    if ($isCompressed && ($option == 0)) {
2893
                        // 1st fragment compressed
2894
                        // this fragment compressed
2895
                        $len = min($charsLeft, $limitpos - $pos);
2896
                        $retstr .= substr($recordData, $pos, $len);
2897
                        $charsLeft -= $len;
2898
                        $isCompressed = true;
2899
                    } elseif (!$isCompressed && ($option != 0)) {
2900
                        // 1st fragment uncompressed
2901
                        // this fragment uncompressed
2902
                        $len = min($charsLeft * 2, $limitpos - $pos);
2903
                        $retstr .= substr($recordData, $pos, $len);
2904
                        $charsLeft -= $len / 2;
2905
                        $isCompressed = false;
2906
                    } elseif (!$isCompressed && ($option == 0)) {
2907
                        // 1st fragment uncompressed
2908
                        // this fragment compressed
2909
                        $len = min($charsLeft, $limitpos - $pos);
2910
                        for ($j = 0; $j < $len; ++$j) {
2911
                            $retstr .= $recordData[$pos + $j]
2912
                            . chr(0);
2913
                        }
2914
                        $charsLeft -= $len;
2915
                        $isCompressed = false;
2916
                    } else {
2917
                        // 1st fragment compressed
2918
                        // this fragment uncompressed
2919
                        $newstr = '';
2920
                        for ($j = 0; $j < strlen($retstr); ++$j) {
2921
                            $newstr .= $retstr[$j] . chr(0);
2922
                        }
2923
                        $retstr = $newstr;
2924
                        $len = min($charsLeft * 2, $limitpos - $pos);
2925
                        $retstr .= substr($recordData, $pos, $len);
2926
                        $charsLeft -= $len / 2;
2927
                        $isCompressed = false;
2928
                    }
2929
2930
                    $pos += $len;
2931
                }
2932
            }
2933
2934
            // convert to UTF-8
2935 4
            $retstr = self::encodeUTF16($retstr, $isCompressed);
2936
2937
            // read additional Rich-Text information, if any
2938 4
            $fmtRuns = [];
2939 4
            if ($hasRichText) {
2940
                // list of formatting runs
2941 2
                for ($j = 0; $j < $formattingRuns; ++$j) {
0 ignored issues
show
Bug introduced by
The variable $formattingRuns does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2942
                    // first formatted character; zero-based
2943 2
                    $charPos = self::getInt2d($recordData, $pos + $j * 4);
2944
2945
                    // index to font record
2946 2
                    $fontIndex = self::getInt2d($recordData, $pos + 2 + $j * 4);
2947
2948 2
                    $fmtRuns[] = [
2949 2
                        'charPos' => $charPos,
2950 2
                        'fontIndex' => $fontIndex,
2951
                    ];
2952
                }
2953 2
                $pos += 4 * $formattingRuns;
2954
            }
2955
2956
            // read additional Asian phonetics information, if any
2957 4
            if ($hasAsian) {
2958
                // For Asian phonetic settings, we skip the extended string data
2959
                $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...
2960
            }
2961
2962
            // store the shared sting
2963 4
            $this->sst[] = [
2964 4
                'value' => $retstr,
2965 4
                'fmtRuns' => $fmtRuns,
2966
            ];
2967
        }
2968
2969
        // getSplicedRecordData() takes care of moving current position in data stream
2970 4
    }
2971
2972
    /**
2973
     * Read PRINTGRIDLINES record.
2974
     */
2975 4
    private function readPrintGridlines()
2976
    {
2977 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2978 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2979
2980
        // move stream pointer to next record
2981 4
        $this->pos += 4 + $length;
2982
2983 4
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
2984
            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
2985 4
            $printGridlines = (bool) self::getInt2d($recordData, 0);
2986 4
            $this->phpSheet->setPrintGridlines($printGridlines);
2987
        }
2988 4
    }
2989
2990
    /**
2991
     * Read DEFAULTROWHEIGHT record.
2992
     */
2993 3 View Code Duplication
    private function readDefaultRowHeight()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2994
    {
2995 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2996 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2997
2998
        // move stream pointer to next record
2999 3
        $this->pos += 4 + $length;
3000
3001
        // offset: 0; size: 2; option flags
3002
        // 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...
3003 3
        $height = self::getInt2d($recordData, 2);
3004 3
        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3005 3
    }
3006
3007
    /**
3008
     * Read SHEETPR record.
3009
     */
3010 4
    private function readSheetPr()
3011
    {
3012 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3013 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3014
3015
        // move stream pointer to next record
3016 4
        $this->pos += 4 + $length;
3017
3018
        // offset: 0; size: 2
3019
3020
        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
3021 4
        $isSummaryBelow = (0x0040 & self::getInt2d($recordData, 0)) >> 6;
3022 4
        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
3023
3024
        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
3025 4
        $isSummaryRight = (0x0080 & self::getInt2d($recordData, 0)) >> 7;
3026 4
        $this->phpSheet->setShowSummaryRight($isSummaryRight);
3027
3028
        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
3029
        // this corresponds to radio button setting in page setup dialog in Excel
3030 4
        $this->isFitToPages = (bool) ((0x0100 & self::getInt2d($recordData, 0)) >> 8);
3031 4
    }
3032
3033
    /**
3034
     * Read HORIZONTALPAGEBREAKS record.
3035
     */
3036 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...
3037
    {
3038
        $length = self::getInt2d($this->data, $this->pos + 2);
3039
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3040
3041
        // move stream pointer to next record
3042
        $this->pos += 4 + $length;
3043
3044
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3045
            // offset: 0; size: 2; number of the following row index structures
3046
            $nm = self::getInt2d($recordData, 0);
3047
3048
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3049
            for ($i = 0; $i < $nm; ++$i) {
3050
                $r = self::getInt2d($recordData, 2 + 6 * $i);
3051
                $cf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3052
                $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...
3053
3054
                // not sure why two column indexes are necessary?
3055
                $this->phpSheet->setBreakByColumnAndRow($cf, $r, \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_ROW);
3056
            }
3057
        }
3058
    }
3059
3060
    /**
3061
     * Read VERTICALPAGEBREAKS record.
3062
     */
3063 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...
3064
    {
3065
        $length = self::getInt2d($this->data, $this->pos + 2);
3066
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3067
3068
        // move stream pointer to next record
3069
        $this->pos += 4 + $length;
3070
3071
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3072
            // offset: 0; size: 2; number of the following column index structures
3073
            $nm = self::getInt2d($recordData, 0);
3074
3075
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3076
            for ($i = 0; $i < $nm; ++$i) {
3077
                $c = self::getInt2d($recordData, 2 + 6 * $i);
3078
                $rf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3079
                $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...
3080
3081
                // not sure why two row indexes are necessary?
3082
                $this->phpSheet->setBreakByColumnAndRow($c, $rf, \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_COLUMN);
3083
            }
3084
        }
3085
    }
3086
3087
    /**
3088
     * Read HEADER record.
3089
     */
3090 4 View Code Duplication
    private function readHeader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3091
    {
3092 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3093 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3094
3095
        // move stream pointer to next record
3096 4
        $this->pos += 4 + $length;
3097
3098 4
        if (!$this->readDataOnly) {
3099
            // offset: 0; size: var
3100
            // realized that $recordData can be empty even when record exists
3101 4
            if ($recordData) {
3102 3
                if ($this->version == self::XLS_BIFF8) {
3103 3
                    $string = self::readUnicodeStringLong($recordData);
3104
                } else {
3105
                    $string = $this->readByteStringShort($recordData);
3106
                }
3107
3108 3
                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
3109 3
                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
3110
            }
3111
        }
3112 4
    }
3113
3114
    /**
3115
     * Read FOOTER record.
3116
     */
3117 4 View Code Duplication
    private function readFooter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3118
    {
3119 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3120 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3121
3122
        // move stream pointer to next record
3123 4
        $this->pos += 4 + $length;
3124
3125 4
        if (!$this->readDataOnly) {
3126
            // offset: 0; size: var
3127
            // realized that $recordData can be empty even when record exists
3128 4
            if ($recordData) {
3129 3
                if ($this->version == self::XLS_BIFF8) {
3130 3
                    $string = self::readUnicodeStringLong($recordData);
3131
                } else {
3132
                    $string = $this->readByteStringShort($recordData);
3133
                }
3134 3
                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
3135 3
                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
3136
            }
3137
        }
3138 4
    }
3139
3140
    /**
3141
     * Read HCENTER record.
3142
     */
3143 4 View Code Duplication
    private function readHcenter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3144
    {
3145 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3146 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3147
3148
        // move stream pointer to next record
3149 4
        $this->pos += 4 + $length;
3150
3151 4
        if (!$this->readDataOnly) {
3152
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3153 4
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3154
3155 4
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3156
        }
3157 4
    }
3158
3159
    /**
3160
     * Read VCENTER record.
3161
     */
3162 4 View Code Duplication
    private function readVcenter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3163
    {
3164 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3165 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3166
3167
        // move stream pointer to next record
3168 4
        $this->pos += 4 + $length;
3169
3170 4
        if (!$this->readDataOnly) {
3171
            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
3172 4
            $isVerticalCentered = (bool) self::getInt2d($recordData, 0);
3173
3174 4
            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3175
        }
3176 4
    }
3177
3178
    /**
3179
     * Read LEFTMARGIN record.
3180
     */
3181 4
    private function readLeftMargin()
3182
    {
3183 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3184 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3185
3186
        // move stream pointer to next record
3187 4
        $this->pos += 4 + $length;
3188
3189 4
        if (!$this->readDataOnly) {
3190
            // offset: 0; size: 8
3191 4
            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3192
        }
3193 4
    }
3194
3195
    /**
3196
     * Read RIGHTMARGIN record.
3197
     */
3198 4
    private function readRightMargin()
3199
    {
3200 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3201 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3202
3203
        // move stream pointer to next record
3204 4
        $this->pos += 4 + $length;
3205
3206 4
        if (!$this->readDataOnly) {
3207
            // offset: 0; size: 8
3208 4
            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3209
        }
3210 4
    }
3211
3212
    /**
3213
     * Read TOPMARGIN record.
3214
     */
3215 4
    private function readTopMargin()
3216
    {
3217 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3218 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3219
3220
        // move stream pointer to next record
3221 4
        $this->pos += 4 + $length;
3222
3223 4
        if (!$this->readDataOnly) {
3224
            // offset: 0; size: 8
3225 4
            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3226
        }
3227 4
    }
3228
3229
    /**
3230
     * Read BOTTOMMARGIN record.
3231
     */
3232 4
    private function readBottomMargin()
3233
    {
3234 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3235 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3236
3237
        // move stream pointer to next record
3238 4
        $this->pos += 4 + $length;
3239
3240 4
        if (!$this->readDataOnly) {
3241
            // offset: 0; size: 8
3242 4
            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3243
        }
3244 4
    }
3245
3246
    /**
3247
     * Read PAGESETUP record.
3248
     */
3249 4
    private function readPageSetup()
3250
    {
3251 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3252 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3253
3254
        // move stream pointer to next record
3255 4
        $this->pos += 4 + $length;
3256
3257 4
        if (!$this->readDataOnly) {
3258
            // offset: 0; size: 2; paper size
3259 4
            $paperSize = self::getInt2d($recordData, 0);
3260
3261
            // offset: 2; size: 2; scaling factor
3262 4
            $scale = self::getInt2d($recordData, 2);
3263
3264
            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
3265 4
            $fitToWidth = self::getInt2d($recordData, 6);
3266
3267
            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
3268 4
            $fitToHeight = self::getInt2d($recordData, 8);
3269
3270
            // offset: 10; size: 2; option flags
3271
3272
            // 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...
3273 4
            $isPortrait = (0x0002 & self::getInt2d($recordData, 10)) >> 1;
3274
3275
            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
3276
            // when this bit is set, do not use flags for those properties
3277 4
            $isNotInit = (0x0004 & self::getInt2d($recordData, 10)) >> 2;
3278
3279 4
            if (!$isNotInit) {
3280 4
                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3281
                switch ($isPortrait) {
3282 4
                    case 0:
3283 2
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
3284 2
                        break;
3285 4
                    case 1:
3286 4
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
3287 4
                        break;
3288
                }
3289
3290 4
                $this->phpSheet->getPageSetup()->setScale($scale, false);
3291 4
                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
3292 4
                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
3293 4
                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
3294
            }
3295
3296
            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
3297 4
            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3298 4
            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3299
3300
            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
3301 4
            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3302 4
            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3303
        }
3304 4
    }
3305
3306
    /**
3307
     * PROTECT - Sheet protection (BIFF2 through BIFF8)
3308
     *   if this record is omitted, then it also means no sheet protection.
3309
     */
3310 1 View Code Duplication
    private function readProtect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3311
    {
3312 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3313 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3314
3315
        // move stream pointer to next record
3316 1
        $this->pos += 4 + $length;
3317
3318 1
        if ($this->readDataOnly) {
3319
            return;
3320
        }
3321
3322
        // 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...
3323
3324
        // bit 0, mask 0x01; 1 = sheet is protected
3325 1
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3326 1
        $this->phpSheet->getProtection()->setSheet((bool) $bool);
3327 1
    }
3328
3329
    /**
3330
     * SCENPROTECT.
3331
     */
3332 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...
3333
    {
3334
        $length = self::getInt2d($this->data, $this->pos + 2);
3335
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3336
3337
        // move stream pointer to next record
3338
        $this->pos += 4 + $length;
3339
3340
        if ($this->readDataOnly) {
3341
            return;
3342
        }
3343
3344
        // 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...
3345
3346
        // bit: 0, mask 0x01; 1 = scenarios are protected
3347
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3348
3349
        $this->phpSheet->getProtection()->setScenarios((bool) $bool);
3350
    }
3351
3352
    /**
3353
     * OBJECTPROTECT.
3354
     */
3355 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...
3356
    {
3357
        $length = self::getInt2d($this->data, $this->pos + 2);
3358
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3359
3360
        // move stream pointer to next record
3361
        $this->pos += 4 + $length;
3362
3363
        if ($this->readDataOnly) {
3364
            return;
3365
        }
3366
3367
        // 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...
3368
3369
        // bit: 0, mask 0x01; 1 = objects are protected
3370
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3371
3372
        $this->phpSheet->getProtection()->setObjects((bool) $bool);
3373
    }
3374
3375
    /**
3376
     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
3377
     */
3378
    private function readPassword()
3379
    {
3380
        $length = self::getInt2d($this->data, $this->pos + 2);
3381
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3382
3383
        // move stream pointer to next record
3384
        $this->pos += 4 + $length;
3385
3386
        if (!$this->readDataOnly) {
3387
            // offset: 0; size: 2; 16-bit hash value of password
3388
            $password = strtoupper(dechex(self::getInt2d($recordData, 0))); // the hashed password
3389
            $this->phpSheet->getProtection()->setPassword($password, true);
3390
        }
3391
    }
3392
3393
    /**
3394
     * Read DEFCOLWIDTH record.
3395
     */
3396 4 View Code Duplication
    private function readDefColWidth()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3397
    {
3398 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3399 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3400
3401
        // move stream pointer to next record
3402 4
        $this->pos += 4 + $length;
3403
3404
        // offset: 0; size: 2; default column width
3405 4
        $width = self::getInt2d($recordData, 0);
3406 4
        if ($width != 8) {
3407
            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3408
        }
3409 4
    }
3410
3411
    /**
3412
     * Read COLINFO record.
3413
     */
3414 3
    private function readColInfo()
3415
    {
3416 3
        $length = self::getInt2d($this->data, $this->pos + 2);
3417 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3418
3419
        // move stream pointer to next record
3420 3
        $this->pos += 4 + $length;
3421
3422 3
        if (!$this->readDataOnly) {
3423
            // offset: 0; size: 2; index to first column in range
3424 3
            $fc = self::getInt2d($recordData, 0); // first column index
3425
3426
            // offset: 2; size: 2; index to last column in range
3427 3
            $lc = self::getInt2d($recordData, 2); // first column index
3428
3429
            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
3430 3
            $width = self::getInt2d($recordData, 4);
3431
3432
            // offset: 6; size: 2; index to XF record for default column formatting
3433 3
            $xfIndex = self::getInt2d($recordData, 6);
3434
3435
            // offset: 8; size: 2; option flags
3436
            // bit: 0; mask: 0x0001; 1= columns are hidden
3437 3
            $isHidden = (0x0001 & self::getInt2d($recordData, 8)) >> 0;
3438
3439
            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
3440 3
            $level = (0x0700 & self::getInt2d($recordData, 8)) >> 8;
3441
3442
            // bit: 12; mask: 0x1000; 1 = collapsed
3443 3
            $isCollapsed = (0x1000 & self::getInt2d($recordData, 8)) >> 12;
3444
3445
            // offset: 10; size: 2; not used
3446
3447 3
            for ($i = $fc; $i <= $lc; ++$i) {
3448 3
                if ($lc == 255 || $lc == 256) {
3449
                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3450
                    break;
3451
                }
3452 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
3453 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
3454 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
3455 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
3456 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3457
            }
3458
        }
3459 3
    }
3460
3461
    /**
3462
     * ROW.
3463
     *
3464
     * This record contains the properties of a single row in a
3465
     * sheet. Rows and cells in a sheet are divided into blocks
3466
     * of 32 rows.
3467
     *
3468
     * --    "OpenOffice.org's Documentation of the Microsoft
3469
     *         Excel File Format"
3470
     */
3471 3
    private function readRow()
3472
    {
3473 3
        $length = self::getInt2d($this->data, $this->pos + 2);
3474 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3475
3476
        // move stream pointer to next record
3477 3
        $this->pos += 4 + $length;
3478
3479 3
        if (!$this->readDataOnly) {
3480
            // offset: 0; size: 2; index of this row
3481 3
            $r = self::getInt2d($recordData, 0);
3482
3483
            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
3484
3485
            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
3486
3487
            // 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...
3488
3489
            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
3490 3
            $height = (0x7FFF & self::getInt2d($recordData, 6)) >> 0;
3491
3492
            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
3493 3
            $useDefaultHeight = (0x8000 & self::getInt2d($recordData, 6)) >> 15;
3494
3495 3
            if (!$useDefaultHeight) {
3496 3
                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
3497
            }
3498
3499
            // offset: 8; size: 2; not used
3500
3501
            // offset: 10; size: 2; not used in BIFF5-BIFF8
3502
3503
            // offset: 12; size: 4; option flags and default row formatting
3504
3505
            // bit: 2-0: mask: 0x00000007; outline level of the row
3506 3
            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3507 3
            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
3508
3509
            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
3510 3
            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
3511 3
            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
3512
3513
            // bit: 5; mask: 0x00000020; 1 = row is hidden
3514 3
            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3515 3
            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
3516
3517
            // bit: 7; mask: 0x00000080; 1 = row has explicit format
3518 3
            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3519
3520
            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
3521 3
            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3522
3523 3
            if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
3524
                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3525
            }
3526
        }
3527 3
    }
3528
3529
    /**
3530
     * Read RK record
3531
     * This record represents a cell that contains an RK value
3532
     * (encoded integer or floating-point value). If a
3533
     * floating-point value cannot be encoded to an RK value,
3534
     * a NUMBER record will be written. This record replaces the
3535
     * record INTEGER written in BIFF2.
3536
     *
3537
     * --    "OpenOffice.org's Documentation of the Microsoft
3538
     *         Excel File Format"
3539
     */
3540 1 View Code Duplication
    private function readRk()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3541
    {
3542 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3543 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3544
3545
        // move stream pointer to next record
3546 1
        $this->pos += 4 + $length;
3547
3548
        // offset: 0; size: 2; index to row
3549 1
        $row = self::getInt2d($recordData, 0);
3550
3551
        // offset: 2; size: 2; index to column
3552 1
        $column = self::getInt2d($recordData, 2);
3553 1
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3554
3555
        // Read cell?
3556 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3557
            // offset: 4; size: 2; index to XF record
3558 1
            $xfIndex = self::getInt2d($recordData, 4);
3559
3560
            // offset: 6; size: 4; RK value
3561 1
            $rknum = self::getInt4d($recordData, 6);
3562 1
            $numValue = self::getIEEE754($rknum);
3563
3564 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3565 1
            if (!$this->readDataOnly) {
3566
                // add style information
3567 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3568
            }
3569
3570
            // add cell
3571 1
            $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3572
        }
3573 1
    }
3574
3575
    /**
3576
     * Read LABELSST record
3577
     * This record represents a cell that contains a string. It
3578
     * replaces the LABEL record and RSTRING record used in
3579
     * BIFF2-BIFF5.
3580
     *
3581
     * --    "OpenOffice.org's Documentation of the Microsoft
3582
     *         Excel File Format"
3583
     */
3584 4
    private function readLabelSst()
3585
    {
3586 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3587 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3588
3589
        // move stream pointer to next record
3590 4
        $this->pos += 4 + $length;
3591
3592
        // offset: 0; size: 2; index to row
3593 4
        $row = self::getInt2d($recordData, 0);
3594
3595
        // offset: 2; size: 2; index to column
3596 4
        $column = self::getInt2d($recordData, 2);
3597 4
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3598
3599 4
        $emptyCell = true;
3600
        // Read cell?
3601 4
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3602
            // offset: 4; size: 2; index to XF record
3603 4
            $xfIndex = self::getInt2d($recordData, 4);
3604
3605
            // offset: 6; size: 4; index to SST record
3606 4
            $index = self::getInt4d($recordData, 6);
3607
3608
            // add cell
3609 4
            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
3610
                // then we should treat as rich text
3611 2
                $richText = new \PhpOffice\PhpSpreadsheet\RichText();
3612 2
                $charPos = 0;
3613 2
                $sstCount = count($this->sst[$index]['fmtRuns']);
3614 2
                for ($i = 0; $i <= $sstCount; ++$i) {
3615 2
                    if (isset($fmtRuns[$i])) {
3616 2
                        $text = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
3617 2
                        $charPos = $fmtRuns[$i]['charPos'];
3618
                    } else {
3619 2
                        $text = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, \PhpOffice\PhpSpreadsheet\Shared\StringHelper::countCharacters($this->sst[$index]['value']));
3620
                    }
3621
3622 2
                    if (\PhpOffice\PhpSpreadsheet\Shared\StringHelper::countCharacters($text) > 0) {
3623 2
                        if ($i == 0) { // first text run, no style
3624 1
                            $richText->createText($text);
3625
                        } else {
3626 2
                            $textRun = $richText->createTextRun($text);
3627 2
                            if (isset($fmtRuns[$i - 1])) {
3628 2
                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
3629 2
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
3630
                                } else {
3631
                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
3632
                                    // check the OpenOffice documentation of the FONT record
3633 2
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
3634
                                }
3635 2
                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
3636
                            }
3637
                        }
3638
                    }
3639
                }
3640 2
                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
3641 2
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3642 2
                    $cell->setValueExplicit($richText, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3643 2
                    $emptyCell = false;
3644
                }
3645
            } else {
3646 4
                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
3647 4
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3648 4
                    $cell->setValueExplicit($this->sst[$index]['value'], \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3649 4
                    $emptyCell = false;
3650
                }
3651
            }
3652
3653 4
            if (!$this->readDataOnly && !$emptyCell) {
3654
                // add style information
3655 4
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
0 ignored issues
show
Bug introduced by
The variable $cell does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3656
            }
3657
        }
3658 4
    }
3659
3660
    /**
3661
     * Read MULRK record
3662
     * This record represents a cell range containing RK value
3663
     * cells. All cells are located in the same row.
3664
     *
3665
     * --    "OpenOffice.org's Documentation of the Microsoft
3666
     *         Excel File Format"
3667
     */
3668
    private function readMulRk()
3669
    {
3670
        $length = self::getInt2d($this->data, $this->pos + 2);
3671
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3672
3673
        // move stream pointer to next record
3674
        $this->pos += 4 + $length;
3675
3676
        // offset: 0; size: 2; index to row
3677
        $row = self::getInt2d($recordData, 0);
3678
3679
        // offset: 2; size: 2; index to first column
3680
        $colFirst = self::getInt2d($recordData, 2);
3681
3682
        // offset: var; size: 2; index to last column
3683
        $colLast = self::getInt2d($recordData, $length - 2);
3684
        $columns = $colLast - $colFirst + 1;
3685
3686
        // offset within record data
3687
        $offset = 4;
3688
3689
        for ($i = 0; $i < $columns; ++$i) {
3690
            $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colFirst + $i);
3691
3692
            // Read cell?
3693
            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3694
                // offset: var; size: 2; index to XF record
3695
                $xfIndex = self::getInt2d($recordData, $offset);
3696
3697
                // offset: var; size: 4; RK value
3698
                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3699
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3700
                if (!$this->readDataOnly) {
3701
                    // add style
3702
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3703
                }
3704
3705
                // add cell value
3706
                $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3707
            }
3708
3709
            $offset += 6;
3710
        }
3711
    }
3712
3713
    /**
3714
     * Read NUMBER record
3715
     * This record represents a cell that contains a
3716
     * floating-point value.
3717
     *
3718
     * --    "OpenOffice.org's Documentation of the Microsoft
3719
     *         Excel File Format"
3720
     */
3721 1 View Code Duplication
    private function readNumber()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3722
    {
3723 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3724 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3725
3726
        // move stream pointer to next record
3727 1
        $this->pos += 4 + $length;
3728
3729
        // offset: 0; size: 2; index to row
3730 1
        $row = self::getInt2d($recordData, 0);
3731
3732
        // offset: 2; size 2; index to column
3733 1
        $column = self::getInt2d($recordData, 2);
3734 1
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3735
3736
        // Read cell?
3737 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3738
            // offset 4; size: 2; index to XF record
3739 1
            $xfIndex = self::getInt2d($recordData, 4);
3740
3741 1
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3742
3743 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3744 1
            if (!$this->readDataOnly) {
3745
                // add cell style
3746 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3747
            }
3748
3749
            // add cell value
3750 1
            $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3751
        }
3752 1
    }
3753
3754
    /**
3755
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3756
     * This record contains the token array and the result of a
3757
     * formula cell.
3758
     *
3759
     * --    "OpenOffice.org's Documentation of the Microsoft
3760
     *         Excel File Format"
3761
     */
3762 2
    private function readFormula()
3763
    {
3764 2
        $length = self::getInt2d($this->data, $this->pos + 2);
3765 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3766
3767
        // move stream pointer to next record
3768 2
        $this->pos += 4 + $length;
3769
3770
        // offset: 0; size: 2; row index
3771 2
        $row = self::getInt2d($recordData, 0);
3772
3773
        // offset: 2; size: 2; col index
3774 2
        $column = self::getInt2d($recordData, 2);
3775 2
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3776
3777
        // offset: 20: size: variable; formula structure
3778 2
        $formulaStructure = substr($recordData, 20);
3779
3780
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3781 2
        $options = self::getInt2d($recordData, 14);
3782
3783
        // bit: 0; mask: 0x0001; 1 = recalculate always
3784
        // bit: 1; mask: 0x0002; 1 = calculate on open
3785
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3786 2
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3787
3788
        // WARNING:
3789
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3790
        // the formula data may be ordinary formula data, therefore we need to check
3791
        // explicitly for the tExp token (0x01)
3792 2
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
3793
3794 2
        if ($isPartOfSharedFormula) {
3795
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3796
            // get the base cell, grab tExp token
3797
            $baseRow = self::getInt2d($formulaStructure, 3);
3798
            $baseCol = self::getInt2d($formulaStructure, 5);
3799
            $this->_baseCell = \PhpOffice\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...
3800
        }
3801
3802
        // Read cell?
3803 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3804 2
            if ($isPartOfSharedFormula) {
3805
                // formula is added to this cell after the sheet has been read
3806
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3807
            }
3808
3809
            // offset: 16: size: 4; not used
3810
3811
            // offset: 4; size: 2; XF index
3812 2
            $xfIndex = self::getInt2d($recordData, 4);
3813
3814
            // offset: 6; size: 8; result of the formula
3815 2
            if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
3816
                // String formula. Result follows in appended STRING record
3817
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING;
3818
3819
                // read possible SHAREDFMLA record
3820
                $code = self::getInt2d($this->data, $this->pos);
3821
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3822
                    $this->readSharedFmla();
3823
                }
3824
3825
                // read STRING record
3826
                $value = $this->readString();
3827 2 View Code Duplication
            } elseif ((ord($recordData[6]) == 1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3828 2
                && (ord($recordData[12]) == 255)
3829 2
                && (ord($recordData[13]) == 255)) {
3830
                // Boolean formula. Result is in +2; 0=false, 1=true
3831
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
3832
                $value = (bool) ord($recordData[8]);
3833 2
            } elseif ((ord($recordData[6]) == 2)
3834 2
                && (ord($recordData[12]) == 255)
3835 2
                && (ord($recordData[13]) == 255)) {
3836
                // Error formula. Error code is in +2
3837
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
3838
                $value = Xls\ErrorCode::lookup(ord($recordData[8]));
3839 2 View Code Duplication
            } elseif ((ord($recordData[6]) == 3)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3840 2
                && (ord($recordData[12]) == 255)
3841 2
                && (ord($recordData[13]) == 255)) {
3842
                // Formula result is a null string
3843 1
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL;
3844 1
                $value = '';
3845
            } else {
3846
                // forumla result is a number, first 14 bytes like _NUMBER record
3847 2
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
3848 2
                $value = self::extractNumber(substr($recordData, 6, 8));
3849
            }
3850
3851 2
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3852 2
            if (!$this->readDataOnly) {
3853
                // add cell style
3854 2
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3855
            }
3856
3857
            // store the formula
3858 2
            if (!$isPartOfSharedFormula) {
3859
                // not part of shared formula
3860
                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
3861
                try {
3862 2
                    if ($this->version != self::XLS_BIFF8) {
3863
                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
3864
                    }
3865 2
                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
3866 2
                    $cell->setValueExplicit('=' . $formula, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
3867
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
3868 2
                    $cell->setValueExplicit($value, $dataType);
3869
                }
3870
            } else {
3871
                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...
3872
                    // do nothing at this point, formula id added later in the code
3873
                } else {
3874
                    $cell->setValueExplicit($value, $dataType);
3875
                }
3876
            }
3877
3878
            // store the cached calculated value
3879 2
            $cell->setCalculatedValue($value);
3880
        }
3881 2
    }
3882
3883
    /**
3884
     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
3885
     * which usually contains relative references.
3886
     * These will be used to construct the formula in each shared formula part after the sheet is read.
3887
     */
3888
    private function readSharedFmla()
3889
    {
3890
        $length = self::getInt2d($this->data, $this->pos + 2);
3891
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3892
3893
        // move stream pointer to next record
3894
        $this->pos += 4 + $length;
3895
3896
        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
3897
        $cellRange = substr($recordData, 0, 6);
3898
        $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...
3899
3900
        // offset: 6, size: 1; not used
3901
3902
        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
3903
        $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...
3904
3905
        // offset: 8, size: var; Binary token array of the shared formula
3906
        $formula = substr($recordData, 8);
3907
3908
        // at this point we only store the shared formula for later use
3909
        $this->sharedFormulas[$this->_baseCell] = $formula;
3910
    }
3911
3912
    /**
3913
     * Read a STRING record from current stream position and advance the stream pointer to next record
3914
     * This record is used for storing result from FORMULA record when it is a string, and
3915
     * it occurs directly after the FORMULA record.
3916
     *
3917
     * @return string The string contents as UTF-8
3918
     */
3919
    private function readString()
3920
    {
3921
        $length = self::getInt2d($this->data, $this->pos + 2);
3922
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3923
3924
        // move stream pointer to next record
3925
        $this->pos += 4 + $length;
3926
3927
        if ($this->version == self::XLS_BIFF8) {
3928
            $string = self::readUnicodeStringLong($recordData);
3929
            $value = $string['value'];
3930
        } else {
3931
            $string = $this->readByteStringLong($recordData);
3932
            $value = $string['value'];
3933
        }
3934
3935
        return $value;
3936
    }
3937
3938
    /**
3939
     * Read BOOLERR record
3940
     * This record represents a Boolean value or error value
3941
     * cell.
3942
     *
3943
     * --    "OpenOffice.org's Documentation of the Microsoft
3944
     *         Excel File Format"
3945
     */
3946
    private function readBoolErr()
3947
    {
3948
        $length = self::getInt2d($this->data, $this->pos + 2);
3949
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3950
3951
        // move stream pointer to next record
3952
        $this->pos += 4 + $length;
3953
3954
        // offset: 0; size: 2; row index
3955
        $row = self::getInt2d($recordData, 0);
3956
3957
        // offset: 2; size: 2; column index
3958
        $column = self::getInt2d($recordData, 2);
3959
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3960
3961
        // Read cell?
3962
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3963
            // offset: 4; size: 2; index to XF record
3964
            $xfIndex = self::getInt2d($recordData, 4);
3965
3966
            // offset: 6; size: 1; the boolean value or error value
3967
            $boolErr = ord($recordData[6]);
3968
3969
            // 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...
3970
            $isError = ord($recordData[7]);
3971
3972
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3973
            switch ($isError) {
3974
                case 0: // boolean
3975
                    $value = (bool) $boolErr;
3976
3977
                    // add cell value
3978
                    $cell->setValueExplicit($value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL);
3979
                    break;
3980
                case 1: // error type
3981
                    $value = Xls\ErrorCode::lookup($boolErr);
3982
3983
                    // add cell value
3984
                    $cell->setValueExplicit($value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR);
3985
                    break;
3986
            }
3987
3988
            if (!$this->readDataOnly) {
3989
                // add cell style
3990
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3991
            }
3992
        }
3993
    }
3994
3995
    /**
3996
     * Read MULBLANK record
3997
     * This record represents a cell range of empty cells. All
3998
     * cells are located in the same row.
3999
     *
4000
     * --    "OpenOffice.org's Documentation of the Microsoft
4001
     *         Excel File Format"
4002
     */
4003 1
    private function readMulBlank()
4004
    {
4005 1
        $length = self::getInt2d($this->data, $this->pos + 2);
4006 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4007
4008
        // move stream pointer to next record
4009 1
        $this->pos += 4 + $length;
4010
4011
        // offset: 0; size: 2; index to row
4012 1
        $row = self::getInt2d($recordData, 0);
4013
4014
        // offset: 2; size: 2; index to first column
4015 1
        $fc = self::getInt2d($recordData, 2);
4016
4017
        // offset: 4; size: 2 x nc; list of indexes to XF records
4018
        // add style information
4019 1
        if (!$this->readDataOnly && $this->readEmptyCells) {
4020 1
            for ($i = 0; $i < $length / 2 - 3; ++$i) {
4021 1
                $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fc + $i);
4022
4023
                // Read cell?
4024 1
                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4025 1
                    $xfIndex = self::getInt2d($recordData, 4 + 2 * $i);
4026 1
                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4027
                }
4028
            }
4029
        }
4030
4031
        // offset: 6; size 2; index to last column (not needed)
4032 1
    }
4033
4034
    /**
4035
     * Read LABEL record
4036
     * This record represents a cell that contains a string. In
4037
     * BIFF8 it is usually replaced by the LABELSST record.
4038
     * Excel still uses this record, if it copies unformatted
4039
     * text cells to the clipboard.
4040
     *
4041
     * --    "OpenOffice.org's Documentation of the Microsoft
4042
     *         Excel File Format"
4043
     */
4044
    private function readLabel()
4045
    {
4046
        $length = self::getInt2d($this->data, $this->pos + 2);
4047
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4048
4049
        // move stream pointer to next record
4050
        $this->pos += 4 + $length;
4051
4052
        // offset: 0; size: 2; index to row
4053
        $row = self::getInt2d($recordData, 0);
4054
4055
        // offset: 2; size: 2; index to column
4056
        $column = self::getInt2d($recordData, 2);
4057
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
4058
4059
        // Read cell?
4060
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4061
            // offset: 4; size: 2; XF index
4062
            $xfIndex = self::getInt2d($recordData, 4);
4063
4064
            // add cell value
4065
            // todo: what if string is very long? continue record
4066 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...
4067
                $string = self::readUnicodeStringLong(substr($recordData, 6));
4068
                $value = $string['value'];
4069
            } else {
4070
                $string = $this->readByteStringLong(substr($recordData, 6));
4071
                $value = $string['value'];
4072
            }
4073
            if ($this->readEmptyCells || trim($value) !== '') {
4074
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4075
                $cell->setValueExplicit($value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
4076
4077
                if (!$this->readDataOnly) {
4078
                    // add cell style
4079
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4080
                }
4081
            }
4082
        }
4083
    }
4084
4085
    /**
4086
     * Read BLANK record.
4087
     */
4088 2
    private function readBlank()
4089
    {
4090 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4091 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4092
4093
        // move stream pointer to next record
4094 2
        $this->pos += 4 + $length;
4095
4096
        // offset: 0; size: 2; row index
4097 2
        $row = self::getInt2d($recordData, 0);
4098
4099
        // offset: 2; size: 2; col index
4100 2
        $col = self::getInt2d($recordData, 2);
4101 2
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($col);
4102
4103
        // Read cell?
4104 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4105
            // offset: 4; size: 2; XF index
4106 2
            $xfIndex = self::getInt2d($recordData, 4);
4107
4108
            // add style information
4109 2
            if (!$this->readDataOnly && $this->readEmptyCells) {
4110 2
                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4111
            }
4112
        }
4113 2
    }
4114
4115
    /**
4116
     * Read MSODRAWING record.
4117
     */
4118 2 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...
4119
    {
4120 2
        $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...
4121
4122
        // get spliced record data
4123 2
        $splicedRecordData = $this->getSplicedRecordData();
4124 2
        $recordData = $splicedRecordData['recordData'];
4125
4126 2
        $this->drawingData .= $recordData;
4127 2
    }
4128
4129
    /**
4130
     * Read OBJ record.
4131
     */
4132 2
    private function readObj()
4133
    {
4134 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4135 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4136
4137
        // move stream pointer to next record
4138 2
        $this->pos += 4 + $length;
4139
4140 2
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4141
            return;
4142
        }
4143
4144
        // recordData consists of an array of subrecords looking like this:
4145
        //    ft: 2 bytes; ftCmo type (0x15)
4146
        //    cb: 2 bytes; size in bytes of ftCmo data
4147
        //    ot: 2 bytes; Object Type
4148
        //    id: 2 bytes; Object id number
4149
        //    grbit: 2 bytes; Option Flags
4150
        //    data: var; subrecord data
4151
4152
        // for now, we are just interested in the second subrecord containing the object type
4153 2
        $ftCmoType = self::getInt2d($recordData, 0);
4154 2
        $cbCmoSize = self::getInt2d($recordData, 2);
4155 2
        $otObjType = self::getInt2d($recordData, 4);
4156 2
        $idObjID = self::getInt2d($recordData, 6);
4157 2
        $grbitOpts = self::getInt2d($recordData, 6);
4158
4159 2
        $this->objs[] = [
4160 2
            'ftCmoType' => $ftCmoType,
4161 2
            'cbCmoSize' => $cbCmoSize,
4162 2
            'otObjType' => $otObjType,
4163 2
            'idObjID' => $idObjID,
4164 2
            'grbitOpts' => $grbitOpts,
4165
        ];
4166 2
        $this->textObjRef = $idObjID;
4167 2
    }
4168
4169
    /**
4170
     * Read WINDOW2 record.
4171
     */
4172 4
    private function readWindow2()
4173
    {
4174 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4175 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4176
4177
        // move stream pointer to next record
4178 4
        $this->pos += 4 + $length;
4179
4180
        // offset: 0; size: 2; option flags
4181 4
        $options = self::getInt2d($recordData, 0);
4182
4183
        // offset: 2; size: 2; index to first visible row
4184 4
        $firstVisibleRow = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$firstVisibleRow is not used, you could remove the assignment.

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

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

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

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

Loading history...
4185
4186
        // offset: 4; size: 2; index to first visible colum
4187 4
        $firstVisibleColumn = self::getInt2d($recordData, 4);
0 ignored issues
show
Unused Code introduced by
$firstVisibleColumn is not used, you could remove the assignment.

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

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

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

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

Loading history...
4188 4
        if ($this->version === self::XLS_BIFF8) {
4189
            // offset:  8; size: 2; not used
4190
            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
4191
            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
4192
            // offset: 14; size: 4; not used
4193 4
            $zoomscaleInPageBreakPreview = self::getInt2d($recordData, 10);
4194 4
            if ($zoomscaleInPageBreakPreview === 0) {
4195 4
                $zoomscaleInPageBreakPreview = 60;
4196
            }
4197 4
            $zoomscaleInNormalView = self::getInt2d($recordData, 12);
4198 4
            if ($zoomscaleInNormalView === 0) {
4199 3
                $zoomscaleInNormalView = 100;
4200
            }
4201
        }
4202
4203
        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
4204 4
        $showGridlines = (bool) ((0x0002 & $options) >> 1);
4205 4
        $this->phpSheet->setShowGridlines($showGridlines);
4206
4207
        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
4208 4
        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4209 4
        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4210
4211
        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
4212 4
        $this->frozen = (bool) ((0x0008 & $options) >> 3);
4213
4214
        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
4215 4
        $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
4216
4217
        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
4218 4
        $isActive = (bool) ((0x0400 & $options) >> 10);
4219 4
        if ($isActive) {
4220 4
            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4221
        }
4222
4223
        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
4224 4
        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4225
4226
        //FIXME: set $firstVisibleRow and $firstVisibleColumn
4227
4228 4
        if ($this->phpSheet->getSheetView()->getView() !== \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT) {
4229
            //NOTE: this setting is inferior to page layout view(Excel2007-)
4230 4
            $view = $isPageBreakPreview ? \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_NORMAL;
4231 4
            $this->phpSheet->getSheetView()->setView($view);
4232 4
            if ($this->version === self::XLS_BIFF8) {
4233 4
                $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
0 ignored issues
show
Bug introduced by
The variable $zoomscaleInPageBreakPreview does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $zoomscaleInNormalView does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4234 4
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4235 4
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4236
            }
4237
        }
4238 4
    }
4239
4240
    /**
4241
     * Read PLV Record(Created by Excel2007 or upper).
4242
     */
4243 4
    private function readPageLayoutView()
4244
    {
4245 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4246 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4247
4248
        // move stream pointer to next record
4249 4
        $this->pos += 4 + $length;
4250
4251
        // offset: 0; size: 2; rt
4252
        //->ignore
4253 4
        $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...
4254
        // offset: 2; size: 2; grbitfr
4255
        //->ignore
4256 4
        $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...
4257
        // offset: 4; size: 8; reserved
4258
        //->ignore
4259
4260
        // offset: 12; size 2; zoom scale
4261 4
        $wScalePLV = self::getInt2d($recordData, 12);
4262
        // offset: 14; size 2; grbit
4263 4
        $grbit = self::getInt2d($recordData, 14);
4264
4265
        // decomprise grbit
4266 4
        $fPageLayoutView = $grbit & 0x01;
4267 4
        $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...
4268 4
        $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...
4269
4270 4
        if ($fPageLayoutView === 1) {
4271
            $this->phpSheet->getSheetView()->setView(\PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT);
4272
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4273
        }
4274
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4275 4
    }
4276
4277
    /**
4278
     * Read SCL record.
4279
     */
4280
    private function readScl()
4281
    {
4282
        $length = self::getInt2d($this->data, $this->pos + 2);
4283
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4284
4285
        // move stream pointer to next record
4286
        $this->pos += 4 + $length;
4287
4288
        // offset: 0; size: 2; numerator of the view magnification
4289
        $numerator = self::getInt2d($recordData, 0);
4290
4291
        // offset: 2; size: 2; numerator of the view magnification
4292
        $denumerator = self::getInt2d($recordData, 2);
4293
4294
        // set the zoom scale (in percent)
4295
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4296
    }
4297
4298
    /**
4299
     * Read PANE record.
4300
     */
4301
    private function readPane()
4302
    {
4303
        $length = self::getInt2d($this->data, $this->pos + 2);
4304
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4305
4306
        // move stream pointer to next record
4307
        $this->pos += 4 + $length;
4308
4309
        if (!$this->readDataOnly) {
4310
            // offset: 0; size: 2; position of vertical split
4311
            $px = self::getInt2d($recordData, 0);
4312
4313
            // offset: 2; size: 2; position of horizontal split
4314
            $py = self::getInt2d($recordData, 2);
4315
4316
            if ($this->frozen) {
4317
                // frozen panes
4318
                $this->phpSheet->freezePane(\PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($px) . ($py + 1));
4319
            }
4320
                // unfrozen panes; split windows; not supported by PhpSpreadsheet core
4321
        }
4322
    }
4323
4324
    /**
4325
     * Read SELECTION record. There is one such record for each pane in the sheet.
4326
     */
4327 4
    private function readSelection()
4328
    {
4329 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4330 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4331
4332
        // move stream pointer to next record
4333 4
        $this->pos += 4 + $length;
4334
4335 4
        if (!$this->readDataOnly) {
4336
            // offset: 0; size: 1; pane identifier
4337 4
            $paneId = ord($recordData[0]);
0 ignored issues
show
Unused Code introduced by
$paneId is not used, you could remove the assignment.

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

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

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

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

Loading history...
4338
4339
            // offset: 1; size: 2; index to row of the active cell
4340 4
            $r = self::getInt2d($recordData, 1);
0 ignored issues
show
Unused Code introduced by
$r is not used, you could remove the assignment.

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

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

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

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

Loading history...
4341
4342
            // offset: 3; size: 2; index to column of the active cell
4343 4
            $c = self::getInt2d($recordData, 3);
0 ignored issues
show
Unused Code introduced by
$c is not used, you could remove the assignment.

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

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

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

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

Loading history...
4344
4345
            // offset: 5; size: 2; index into the following cell range list to the
4346
            //  entry that contains the active cell
4347 4
            $index = self::getInt2d($recordData, 5);
0 ignored issues
show
Unused Code introduced by
$index is not used, you could remove the assignment.

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

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

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

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

Loading history...
4348
4349
            // offset: 7; size: var; cell range address list containing all selected cell ranges
4350 4
            $data = substr($recordData, 7);
4351 4
            $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
4352
4353 4
            $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
4354
4355
            // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
4356 4
            if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
4357
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
4358
            }
4359
4360
            // first row '1' + last row '65536' indicates that full column is selected
4361 4
            if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
4362
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
4363
            }
4364
4365
            // first column 'A' + last column 'IV' indicates that full row is selected
4366 4
            if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
4367
                $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
4368
            }
4369
4370 4
            $this->phpSheet->setSelectedCells($selectedCells);
4371
        }
4372 4
    }
4373
4374 2
    private function includeCellRangeFiltered($cellRangeAddress)
4375
    {
4376 2
        $includeCellRange = true;
4377 2
        if ($this->getReadFilter() !== null) {
4378 2
            $includeCellRange = false;
4379 2
            $rangeBoundaries = \PhpOffice\PhpSpreadsheet\Cell::getRangeBoundaries($cellRangeAddress);
4380 2
            ++$rangeBoundaries[1][0];
4381 2
            for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
4382 2
                for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
4383 2
                    if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
4384 2
                        $includeCellRange = true;
4385 2
                        break 2;
4386
                    }
4387
                }
4388
            }
4389
        }
4390
4391 2
        return $includeCellRange;
4392
    }
4393
4394
    /**
4395
     * MERGEDCELLS.
4396
     *
4397
     * This record contains the addresses of merged cell ranges
4398
     * in the current sheet.
4399
     *
4400
     * --    "OpenOffice.org's Documentation of the Microsoft
4401
     *         Excel File Format"
4402
     */
4403 2
    private function readMergedCells()
4404
    {
4405 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4406 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4407
4408
        // move stream pointer to next record
4409 2
        $this->pos += 4 + $length;
4410
4411 2
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
4412 2
            $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
4413 2
            foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
4414 2
                if ((strpos($cellRangeAddress, ':') !== false) &&
4415 2
                    ($this->includeCellRangeFiltered($cellRangeAddress))) {
4416 2
                    $this->phpSheet->mergeCells($cellRangeAddress);
4417
                }
4418
            }
4419
        }
4420 2
    }
4421
4422
    /**
4423
     * Read HYPERLINK record.
4424
     */
4425 2
    private function readHyperLink()
4426
    {
4427 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4428 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4429
4430
        // move stream pointer forward to next record
4431 2
        $this->pos += 4 + $length;
4432
4433 2
        if (!$this->readDataOnly) {
4434
            // offset: 0; size: 8; cell range address of all cells containing this hyperlink
4435
            try {
4436 2
                $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
4437
            } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4438
                return;
4439
            }
4440
4441
            // offset: 8, size: 16; GUID of StdLink
4442
4443
            // offset: 24, size: 4; unknown value
4444
4445
            // offset: 28, size: 4; option flags
4446
            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
4447 2
            $isFileLinkOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 0;
4448
4449
            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
4450 2
            $isAbsPathOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 1;
0 ignored issues
show
Unused Code introduced by
$isAbsPathOrUrl is not used, you could remove the assignment.

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

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

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

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

Loading history...
4451
4452
            // 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...
4453 2
            $hasDesc = (0x00000014 & self::getInt2d($recordData, 28)) >> 2;
4454
4455
            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
4456 2
            $hasText = (0x00000008 & self::getInt2d($recordData, 28)) >> 3;
4457
4458
            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
4459 2
            $hasFrame = (0x00000080 & self::getInt2d($recordData, 28)) >> 7;
4460
4461
            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
4462 2
            $isUNC = (0x00000100 & self::getInt2d($recordData, 28)) >> 8;
4463
4464
            // offset within record data
4465 2
            $offset = 32;
4466
4467 2
            if ($hasDesc) {
4468
                // offset: 32; size: var; character count of description text
4469 1
                $dl = self::getInt4d($recordData, 32);
4470
                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
4471 1
                $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
0 ignored issues
show
Unused Code introduced by
$desc is not used, you could remove the assignment.

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

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

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

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

Loading history...
4472 1
                $offset += 4 + 2 * $dl;
4473
            }
4474 2
            if ($hasFrame) {
4475
                $fl = self::getInt4d($recordData, $offset);
4476
                $offset += 4 + 2 * $fl;
4477
            }
4478
4479
            // detect type of hyperlink (there are 4 types)
4480 2
            $hyperlinkType = null;
4481
4482 2
            if ($isUNC) {
4483
                $hyperlinkType = 'UNC';
4484 2
            } elseif (!$isFileLinkOrUrl) {
4485 2
                $hyperlinkType = 'workbook';
4486 2
            } elseif (ord($recordData[$offset]) == 0x03) {
4487
                $hyperlinkType = 'local';
4488 2
            } elseif (ord($recordData[$offset]) == 0xE0) {
4489 2
                $hyperlinkType = 'URL';
4490
            }
4491
4492
            switch ($hyperlinkType) {
4493 2
                case 'URL':
4494
                    // section 5.58.2: Hyperlink containing a URL
4495
                    // e.g. http://example.org/index.php
4496
4497
                    // offset: var; size: 16; GUID of URL Moniker
4498 2
                    $offset += 16;
4499
                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
4500 2
                    $us = self::getInt4d($recordData, $offset);
4501 2
                    $offset += 4;
4502
                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
4503 2
                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
4504 2
                    $nullOffset = strpos($url, 0x00);
4505 2
                    if ($nullOffset) {
4506 1
                        $url = substr($url, 0, $nullOffset);
4507
                    }
4508 2
                    $url .= $hasText ? '#' : '';
4509 2
                    $offset += $us;
4510 2
                    break;
4511 2
                case 'local':
4512
                    // section 5.58.3: Hyperlink to local file
4513
                    // examples:
4514
                    //   mydoc.txt
4515
                    //   ../../somedoc.xls#Sheet!A1
4516
4517
                    // offset: var; size: 16; GUI of File Moniker
4518
                    $offset += 16;
4519
4520
                    // offset: var; size: 2; directory up-level count.
4521
                    $upLevelCount = self::getInt2d($recordData, $offset);
4522
                    $offset += 2;
4523
4524
                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
4525
                    $sl = self::getInt4d($recordData, $offset);
4526
                    $offset += 4;
4527
4528
                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
4529
                    $shortenedFilePath = substr($recordData, $offset, $sl);
4530
                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
4531
                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
4532
4533
                    $offset += $sl;
4534
4535
                    // offset: var; size: 24; unknown sequence
4536
                    $offset += 24;
4537
4538
                    // extended file path
4539
                    // offset: var; size: 4; size of the following file link field including string lenth mark
4540
                    $sz = self::getInt4d($recordData, $offset);
4541
                    $offset += 4;
4542
4543
                    // only present if $sz > 0
4544
                    if ($sz > 0) {
4545
                        // offset: var; size: 4; size of the character array of the extended file path and name
4546
                        $xl = self::getInt4d($recordData, $offset);
4547
                        $offset += 4;
4548
4549
                        // offset: var; size 2; unknown
4550
                        $offset += 2;
4551
4552
                        // offset: var; size $xl; character array of the extended file path and name.
4553
                        $extendedFilePath = substr($recordData, $offset, $xl);
4554
                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
4555
                        $offset += $xl;
4556
                    }
4557
4558
                    // construct the path
4559
                    $url = str_repeat('..\\', $upLevelCount);
4560
                    $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...
4561
                    $url .= $hasText ? '#' : '';
4562
4563
                    break;
4564 2
                case 'UNC':
4565
                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
4566
                    // todo: implement
4567
                    return;
4568 2
                case 'workbook':
4569
                    // section 5.58.5: Hyperlink to the Current Workbook
4570
                    // e.g. Sheet2!B1:C2, stored in text mark field
4571 2
                    $url = 'sheet://';
4572 2
                    break;
4573
                default:
4574
                    return;
4575
            }
4576
4577 2
            if ($hasText) {
4578
                // offset: var; size: 4; character count of text mark including trailing zero word
4579 2
                $tl = self::getInt4d($recordData, $offset);
4580 2
                $offset += 4;
4581
                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
4582 2
                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
4583 2
                $url .= $text;
4584
            }
4585
4586
            // apply the hyperlink to all the relevant cells
4587 2
            foreach (\PhpOffice\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
4588 2
                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
4589
            }
4590
        }
4591 2
    }
4592
4593
    /**
4594
     * Read DATAVALIDATIONS record.
4595
     */
4596
    private function readDataValidations()
4597
    {
4598
        $length = self::getInt2d($this->data, $this->pos + 2);
4599
        $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...
4600
4601
        // move stream pointer forward to next record
4602
        $this->pos += 4 + $length;
4603
    }
4604
4605
    /**
4606
     * Read DATAVALIDATION record.
4607
     */
4608
    private function readDataValidation()
4609
    {
4610
        $length = self::getInt2d($this->data, $this->pos + 2);
4611
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4612
4613
        // move stream pointer forward to next record
4614
        $this->pos += 4 + $length;
4615
4616
        if ($this->readDataOnly) {
4617
            return;
4618
        }
4619
4620
        // offset: 0; size: 4; Options
4621
        $options = self::getInt4d($recordData, 0);
4622
4623
        // 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...
4624
        $type = (0x0000000F & $options) >> 0;
4625 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...
4626
            case 0x00:
4627
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_NONE;
4628
                break;
4629
            case 0x01:
4630
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE;
4631
                break;
4632
            case 0x02:
4633
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_DECIMAL;
4634
                break;
4635
            case 0x03:
4636
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST;
4637
                break;
4638
            case 0x04:
4639
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_DATE;
4640
                break;
4641
            case 0x05:
4642
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_TIME;
4643
                break;
4644
            case 0x06:
4645
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_TEXTLENGTH;
4646
                break;
4647
            case 0x07:
4648
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_CUSTOM;
4649
                break;
4650
        }
4651
4652
        // bit: 4-6; mask: 0x00000070; error type
4653
        $errorStyle = (0x00000070 & $options) >> 4;
4654 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...
4655
            case 0x00:
4656
                $errorStyle = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_STOP;
4657
                break;
4658
            case 0x01:
4659
                $errorStyle = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_WARNING;
4660
                break;
4661
            case 0x02:
4662
                $errorStyle = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION;
4663
                break;
4664
        }
4665
4666
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
4667
        // I have only seen cases where this is 1
4668
        $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...
4669
4670
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
4671
        $allowBlank = (0x00000100 & $options) >> 8;
4672
4673
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
4674
        $suppressDropDown = (0x00000200 & $options) >> 9;
4675
4676
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
4677
        $showInputMessage = (0x00040000 & $options) >> 18;
4678
4679
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
4680
        $showErrorMessage = (0x00080000 & $options) >> 19;
4681
4682
        // bit: 20-23; mask: 0x00F00000; condition operator
4683
        $operator = (0x00F00000 & $options) >> 20;
4684 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...
4685
            case 0x00:
4686
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_BETWEEN;
4687
                break;
4688
            case 0x01:
4689
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTBETWEEN;
4690
                break;
4691
            case 0x02:
4692
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_EQUAL;
4693
                break;
4694
            case 0x03:
4695
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTEQUAL;
4696
                break;
4697
            case 0x04:
4698
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHAN;
4699
                break;
4700
            case 0x05:
4701
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHAN;
4702
                break;
4703
            case 0x06:
4704
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHANOREQUAL;
4705
                break;
4706
            case 0x07:
4707
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHANOREQUAL;
4708
                break;
4709
        }
4710
4711
        // offset: 4; size: var; title of the prompt box
4712
        $offset = 4;
4713
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4714
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4715
        $offset += $string['size'];
4716
4717
        // offset: var; size: var; title of the error box
4718
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4719
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4720
        $offset += $string['size'];
4721
4722
        // offset: var; size: var; text of the prompt box
4723
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4724
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
4725
        $offset += $string['size'];
4726
4727
        // offset: var; size: var; text of the error box
4728
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4729
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
4730
        $offset += $string['size'];
4731
4732
        // offset: var; size: 2; size of the formula data for the first condition
4733
        $sz1 = self::getInt2d($recordData, $offset);
4734
        $offset += 2;
4735
4736
        // offset: var; size: 2; not used
4737
        $offset += 2;
4738
4739
        // offset: var; size: $sz1; formula data for first condition (without size field)
4740
        $formula1 = substr($recordData, $offset, $sz1);
4741
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
4742
        try {
4743
            $formula1 = $this->getFormulaFromStructure($formula1);
4744
4745
            // in list type validity, null characters are used as item separators
4746
            if ($type == \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST) {
4747
                $formula1 = str_replace(chr(0), ',', $formula1);
4748
            }
4749
        } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4750
            return;
4751
        }
4752
        $offset += $sz1;
4753
4754
        // offset: var; size: 2; size of the formula data for the first condition
4755
        $sz2 = self::getInt2d($recordData, $offset);
4756
        $offset += 2;
4757
4758
        // offset: var; size: 2; not used
4759
        $offset += 2;
4760
4761
        // offset: var; size: $sz2; formula data for second condition (without size field)
4762
        $formula2 = substr($recordData, $offset, $sz2);
4763
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
4764
        try {
4765
            $formula2 = $this->getFormulaFromStructure($formula2);
4766
        } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4767
            return;
4768
        }
4769
        $offset += $sz2;
4770
4771
        // offset: var; size: var; cell range address list with
4772
        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
4773
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
4774
4775
        foreach ($cellRangeAddresses as $cellRange) {
4776
            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4777
            foreach (\PhpOffice\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($stRange) as $coordinate) {
4778
                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4779
                $objValidation->setType($type);
4780
                $objValidation->setErrorStyle($errorStyle);
4781
                $objValidation->setAllowBlank((bool) $allowBlank);
4782
                $objValidation->setShowInputMessage((bool) $showInputMessage);
4783
                $objValidation->setShowErrorMessage((bool) $showErrorMessage);
4784
                $objValidation->setShowDropDown(!$suppressDropDown);
4785
                $objValidation->setOperator($operator);
4786
                $objValidation->setErrorTitle($errorTitle);
4787
                $objValidation->setError($error);
4788
                $objValidation->setPromptTitle($promptTitle);
4789
                $objValidation->setPrompt($prompt);
4790
                $objValidation->setFormula1($formula1);
4791
                $objValidation->setFormula2($formula2);
4792
            }
4793
        }
4794
    }
4795
4796
    /**
4797
     * Read SHEETLAYOUT record. Stores sheet tab color information.
4798
     */
4799 2
    private function readSheetLayout()
4800
    {
4801 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4802 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4803
4804
        // move stream pointer to next record
4805 2
        $this->pos += 4 + $length;
4806
4807
        // local pointer in record data
4808 2
        $offset = 0;
0 ignored issues
show
Unused Code introduced by
$offset is not used, you could remove the assignment.

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

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

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

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

Loading history...
4809
4810 2
        if (!$this->readDataOnly) {
4811
            // offset: 0; size: 2; repeated record identifier 0x0862
4812
4813
            // offset: 2; size: 10; not used
4814
4815
            // offset: 12; size: 4; size of record data
4816
            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
4817 2
            $sz = self::getInt4d($recordData, 12);
4818
4819
            switch ($sz) {
4820 2
                case 0x14:
4821
                    // offset: 16; size: 2; color index for sheet tab
4822 1
                    $colorIndex = self::getInt2d($recordData, 16);
4823 1
                    $color = Xls\Color::map($colorIndex, $this->palette, $this->version);
4824 1
                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
4825 1
                    break;
4826 1
                case 0x28:
4827
                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
4828 1
                    return;
4829
                    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...
4830
            }
4831
        }
4832 1
    }
4833
4834
    /**
4835
     * Read SHEETPROTECTION record (FEATHEADR).
4836
     */
4837 4
    private function readSheetProtection()
4838
    {
4839 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4840 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4841
4842
        // move stream pointer to next record
4843 4
        $this->pos += 4 + $length;
4844
4845 4
        if ($this->readDataOnly) {
4846
            return;
4847
        }
4848
4849
        // offset: 0; size: 2; repeated record header
4850
4851
        // offset: 2; size: 2; FRT cell reference flag (=0 currently)
4852
4853
        // offset: 4; size: 8; Currently not used and set to 0
4854
4855
        // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
4856 4
        $isf = self::getInt2d($recordData, 12);
4857 4
        if ($isf != 2) {
4858
            return;
4859
        }
4860
4861
        // offset: 14; size: 1; =1 since this is a feat header
4862
4863
        // offset: 15; size: 4; size of rgbHdrSData
4864
4865
        // rgbHdrSData, assume "Enhanced Protection"
4866
        // offset: 19; size: 2; option flags
4867 4
        $options = self::getInt2d($recordData, 19);
4868
4869
        // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
4870 4
        $bool = (0x0001 & $options) >> 0;
4871 4
        $this->phpSheet->getProtection()->setObjects(!$bool);
4872
4873
        // bit: 1; mask 0x0002; edit scenarios
4874 4
        $bool = (0x0002 & $options) >> 1;
4875 4
        $this->phpSheet->getProtection()->setScenarios(!$bool);
4876
4877
        // bit: 2; mask 0x0004; format cells
4878 4
        $bool = (0x0004 & $options) >> 2;
4879 4
        $this->phpSheet->getProtection()->setFormatCells(!$bool);
4880
4881
        // bit: 3; mask 0x0008; format columns
4882 4
        $bool = (0x0008 & $options) >> 3;
4883 4
        $this->phpSheet->getProtection()->setFormatColumns(!$bool);
4884
4885
        // bit: 4; mask 0x0010; format rows
4886 4
        $bool = (0x0010 & $options) >> 4;
4887 4
        $this->phpSheet->getProtection()->setFormatRows(!$bool);
4888
4889
        // bit: 5; mask 0x0020; insert columns
4890 4
        $bool = (0x0020 & $options) >> 5;
4891 4
        $this->phpSheet->getProtection()->setInsertColumns(!$bool);
4892
4893
        // bit: 6; mask 0x0040; insert rows
4894 4
        $bool = (0x0040 & $options) >> 6;
4895 4
        $this->phpSheet->getProtection()->setInsertRows(!$bool);
4896
4897
        // bit: 7; mask 0x0080; insert hyperlinks
4898 4
        $bool = (0x0080 & $options) >> 7;
4899 4
        $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
4900
4901
        // bit: 8; mask 0x0100; delete columns
4902 4
        $bool = (0x0100 & $options) >> 8;
4903 4
        $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
4904
4905
        // bit: 9; mask 0x0200; delete rows
4906 4
        $bool = (0x0200 & $options) >> 9;
4907 4
        $this->phpSheet->getProtection()->setDeleteRows(!$bool);
4908
4909
        // bit: 10; mask 0x0400; select locked cells
4910 4
        $bool = (0x0400 & $options) >> 10;
4911 4
        $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
4912
4913
        // bit: 11; mask 0x0800; sort cell range
4914 4
        $bool = (0x0800 & $options) >> 11;
4915 4
        $this->phpSheet->getProtection()->setSort(!$bool);
4916
4917
        // bit: 12; mask 0x1000; auto filter
4918 4
        $bool = (0x1000 & $options) >> 12;
4919 4
        $this->phpSheet->getProtection()->setAutoFilter(!$bool);
4920
4921
        // bit: 13; mask 0x2000; pivot tables
4922 4
        $bool = (0x2000 & $options) >> 13;
4923 4
        $this->phpSheet->getProtection()->setPivotTables(!$bool);
4924
4925
        // bit: 14; mask 0x4000; select unlocked cells
4926 4
        $bool = (0x4000 & $options) >> 14;
4927 4
        $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
4928
4929
        // offset: 21; size: 2; not used
4930 4
    }
4931
4932
    /**
4933
     * Read RANGEPROTECTION record
4934
     * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
4935
     * where it is referred to as FEAT record.
4936
     */
4937 1
    private function readRangeProtection()
4938
    {
4939 1
        $length = self::getInt2d($this->data, $this->pos + 2);
4940 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4941
4942
        // move stream pointer to next record
4943 1
        $this->pos += 4 + $length;
4944
4945
        // local pointer in record data
4946 1
        $offset = 0;
4947
4948 1
        if (!$this->readDataOnly) {
4949 1
            $offset += 12;
4950
4951
            // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
4952 1
            $isf = self::getInt2d($recordData, 12);
4953 1
            if ($isf != 2) {
4954
                // we only read FEAT records of type 2
4955
                return;
4956
            }
4957 1
            $offset += 2;
4958
4959 1
            $offset += 5;
4960
4961
            // offset: 19; size: 2; count of ref ranges this feature is on
4962 1
            $cref = self::getInt2d($recordData, 19);
4963 1
            $offset += 2;
4964
4965 1
            $offset += 6;
4966
4967
            // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
4968 1
            $cellRanges = [];
4969 1
            for ($i = 0; $i < $cref; ++$i) {
4970
                try {
4971 1
                    $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
4972
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4973
                    return;
4974
                }
4975 1
                $cellRanges[] = $cellRange;
4976 1
                $offset += 8;
4977
            }
4978
4979
            // offset: var; size: var; variable length of feature specific data
4980 1
            $rgbFeat = substr($recordData, $offset);
0 ignored issues
show
Unused Code introduced by
$rgbFeat is not used, you could remove the assignment.

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

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

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

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

Loading history...
4981 1
            $offset += 4;
4982
4983
            // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
4984 1
            $wPassword = self::getInt4d($recordData, $offset);
4985 1
            $offset += 4;
4986
4987
            // Apply range protection to sheet
4988 1
            if ($cellRanges) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cellRanges of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
4989 1
                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
4990
            }
4991
        }
4992 1
    }
4993
4994
    /**
4995
     * Read IMDATA record.
4996
     */
4997
    private function readImData()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
4998
    {
4999
        $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...
5000
5001
        // get spliced record data
5002
        $splicedRecordData = $this->getSplicedRecordData();
5003
        $recordData = $splicedRecordData['recordData'];
5004
5005
        // UNDER CONSTRUCTION
5006
5007
        // offset: 0; size: 2; image format
5008
        $cf = self::getInt2d($recordData, 0);
5009
5010
        // offset: 2; size: 2; environment from which the file was written
5011
        $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...
5012
5013
        // offset: 4; size: 4; length of the image data
5014
        $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...
5015
5016
        // offset: 8; size: var; image data
5017
        $iData = substr($recordData, 8);
5018
5019
        switch ($cf) {
5020
            case 0x09: // Windows bitmap format
5021
                // BITMAPCOREINFO
5022
                // 1. BITMAPCOREHEADER
5023
                // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
5024
                $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...
5025
5026
                // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
5027
                $bcWidth = self::getInt2d($iData, 4);
5028
5029
                // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
5030
                $bcHeight = self::getInt2d($iData, 6);
5031
                $ih = imagecreatetruecolor($bcWidth, $bcHeight);
5032
5033
                // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
5034
5035
                // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
5036
                $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...
5037
5038
                $rgbString = substr($iData, 12);
5039
                $rgbTriples = [];
5040
                while (strlen($rgbString) > 0) {
5041
                    $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
5042
                    $rgbString = substr($rgbString, 3);
5043
                }
5044
                $x = 0;
5045
                $y = 0;
5046
                foreach ($rgbTriples as $i => $rgbTriple) {
5047
                    $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
5048
                    imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
5049
                    $x = ($x + 1) % $bcWidth;
5050
                    $y = $y + floor(($x + 1) / $bcWidth);
5051
                }
5052
                //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...
5053
5054
                $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
5055
                $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...
5056
                $drawing->setWorksheet($this->phpSheet);
5057
                break;
5058
            case 0x02: // Windows metafile or Macintosh PICT format
5059
            case 0x0e: // native format
5060
            default:
5061
                break;
5062
        }
5063
5064
        // getSplicedRecordData() takes care of moving current position in data stream
5065
    }
5066
5067
    /**
5068
     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
5069
     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
5070
     * In this case, we must treat the CONTINUE record as a MSODRAWING record.
5071
     */
5072
    private function readContinue()
5073
    {
5074
        $length = self::getInt2d($this->data, $this->pos + 2);
5075
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5076
5077
        // check if we are reading drawing data
5078
        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
5079
        if ($this->drawingData == '') {
5080
            // move stream pointer to next record
5081
            $this->pos += 4 + $length;
5082
5083
            return;
5084
        }
5085
5086
        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
5087
        if ($length < 4) {
5088
            // move stream pointer to next record
5089
            $this->pos += 4 + $length;
5090
5091
            return;
5092
        }
5093
5094
        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
5095
        // look inside CONTINUE record to see if it looks like a part of an Escher stream
5096
        // we know that Escher stream may be split at least at
5097
        //        0xF003 MsofbtSpgrContainer
5098
        //        0xF004 MsofbtSpContainer
5099
        //        0xF00D MsofbtClientTextbox
5100
        $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
5101
5102
        $splitPoint = self::getInt2d($recordData, 2);
5103
        if (in_array($splitPoint, $validSplitPoints)) {
5104
            // get spliced record data (and move pointer to next record)
5105
            $splicedRecordData = $this->getSplicedRecordData();
5106
            $this->drawingData .= $splicedRecordData['recordData'];
5107
5108
            return;
5109
        }
5110
5111
        // move stream pointer to next record
5112
        $this->pos += 4 + $length;
5113
    }
5114
5115
    /**
5116
     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
5117
     * records are found. Splices the record data pieces and returns the combined string as if record data
5118
     * is in one piece.
5119
     * Moves to next current position in data stream to start of next record different from a CONtINUE record.
5120
     *
5121
     * @return array
5122
     */
5123 4
    private function getSplicedRecordData()
5124
    {
5125 4
        $data = '';
5126 4
        $spliceOffsets = [];
5127
5128 4
        $i = 0;
5129 4
        $spliceOffsets[0] = 0;
5130
5131
        do {
5132 4
            ++$i;
5133
5134
            // offset: 0; size: 2; identifier
5135 4
            $identifier = self::getInt2d($this->data, $this->pos);
0 ignored issues
show
Unused Code introduced by
$identifier is not used, you could remove the assignment.

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

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

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

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

Loading history...
5136
            // offset: 2; size: 2; length
5137 4
            $length = self::getInt2d($this->data, $this->pos + 2);
5138 4
            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
5139
5140 4
            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
5141
5142 4
            $this->pos += 4 + $length;
5143 4
            $nextIdentifier = self::getInt2d($this->data, $this->pos);
5144 4
        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5145
5146
        $splicedData = [
5147 4
            'recordData' => $data,
5148 4
            'spliceOffsets' => $spliceOffsets,
5149
        ];
5150
5151 4
        return $splicedData;
5152
    }
5153
5154
    /**
5155
     * Convert formula structure into human readable Excel formula like 'A3+A5*5'.
5156
     *
5157
     * @param string $formulaStructure The complete binary data for the formula
5158
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5159
     *
5160
     * @return string Human readable formula
5161
     */
5162 2
    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
5163
    {
5164
        // offset: 0; size: 2; size of the following formula data
5165 2
        $sz = self::getInt2d($formulaStructure, 0);
5166
5167
        // offset: 2; size: sz
5168 2
        $formulaData = substr($formulaStructure, 2, $sz);
5169
5170
        // offset: 2 + sz; size: variable (optional)
5171 2
        if (strlen($formulaStructure) > 2 + $sz) {
5172
            $additionalData = substr($formulaStructure, 2 + $sz);
5173
        } else {
5174 2
            $additionalData = '';
5175
        }
5176
5177 2
        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
5178
    }
5179
5180
    /**
5181
     * Take formula data and additional data for formula and return human readable formula.
5182
     *
5183
     * @param string $formulaData The binary data for the formula itself
5184
     * @param string $additionalData Additional binary data going with the formula
5185
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5186
     *
5187
     * @return string Human readable formula
5188
     */
5189 2
    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
5190
    {
5191
        // start parsing the formula data
5192 2
        $tokens = [];
5193
5194 2
        while (strlen($formulaData) > 0 and $token = $this->getNextToken($formulaData, $baseCell)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
5195 2
            $tokens[] = $token;
5196 2
            $formulaData = substr($formulaData, $token['size']);
5197
        }
5198
5199 2
        $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
5200
5201 2
        return $formulaString;
5202
    }
5203
5204
    /**
5205
     * Take array of tokens together with additional data for formula and return human readable formula.
5206
     *
5207
     * @param array $tokens
5208
     * @param string $additionalData Additional binary data going with the formula
5209
     *
5210
     * @return string Human readable formula
5211
     */
5212 2
    private function createFormulaFromTokens($tokens, $additionalData)
5213
    {
5214
        // empty formula?
5215 2
        if (empty($tokens)) {
5216
            return '';
5217
        }
5218
5219 2
        $formulaStrings = [];
5220 2
        foreach ($tokens as $token) {
5221
            // initialize spaces
5222 2
            $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
5223 2
            $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
5224 2
            $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
5225 2
            $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
5226 2
            $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
5227 2
            $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
5228
5229 2
            switch ($token['name']) {
5230 2
                case 'tAdd': // addition
5231 2
                case 'tConcat': // addition
5232 2
                case 'tDiv': // division
5233 2
                case 'tEQ': // equality
5234 2
                case 'tGE': // greater than or equal
5235 2
                case 'tGT': // greater than
5236 2
                case 'tIsect': // intersection
5237 2
                case 'tLE': // less than or equal
5238 2
                case 'tList': // less than or equal
5239 2
                case 'tLT': // less than
5240 2
                case 'tMul': // multiplication
5241 2
                case 'tNE': // multiplication
5242 2
                case 'tPower': // power
5243 2
                case 'tRange': // range
5244 2
                case 'tSub': // subtraction
5245 2
                    $op2 = array_pop($formulaStrings);
5246 2
                    $op1 = array_pop($formulaStrings);
5247 2
                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
5248 2
                    unset($space0, $space1);
5249 2
                    break;
5250 2
                case 'tUplus': // unary plus
5251 2 View Code Duplication
                case 'tUminus': // unary minus
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5252
                    $op = array_pop($formulaStrings);
5253
                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
5254
                    unset($space0, $space1);
5255
                    break;
5256 2 View Code Duplication
                case 'tPercent': // percent sign
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5257
                    $op = array_pop($formulaStrings);
5258
                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
5259
                    unset($space0, $space1);
5260
                    break;
5261 2
                case 'tAttrVolatile': // indicates volatile function
5262 2
                case 'tAttrIf':
5263 2
                case 'tAttrSkip':
5264 2
                case 'tAttrChoose':
5265
                    // token is only important for Excel formula evaluator
5266
                    // do nothing
5267
                    break;
5268 2
                case 'tAttrSpace': // space / carriage return
5269
                    // space will be used when next token arrives, do not alter formulaString stack
5270
                    switch ($token['data']['spacetype']) {
5271
                        case 'type0':
5272
                            $space0 = str_repeat(' ', $token['data']['spacecount']);
5273
                            break;
5274
                        case 'type1':
5275
                            $space1 = str_repeat("\n", $token['data']['spacecount']);
5276
                            break;
5277
                        case 'type2':
5278
                            $space2 = str_repeat(' ', $token['data']['spacecount']);
5279
                            break;
5280
                        case 'type3':
5281
                            $space3 = str_repeat("\n", $token['data']['spacecount']);
5282
                            break;
5283
                        case 'type4':
5284
                            $space4 = str_repeat(' ', $token['data']['spacecount']);
5285
                            break;
5286
                        case 'type5':
5287
                            $space5 = str_repeat("\n", $token['data']['spacecount']);
5288
                            break;
5289
                    }
5290
                    break;
5291 2
                case 'tAttrSum': // SUM function with one parameter
5292 1
                    $op = array_pop($formulaStrings);
5293 1
                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
5294 1
                    unset($space0, $space1);
5295 1
                    break;
5296 2
                case 'tFunc': // function with fixed number of arguments
5297 2
                case 'tFuncV': // function with variable number of arguments
5298 1
                    if ($token['data']['function'] != '') {
5299
                        // normal function
5300 1
                        $ops = []; // array of operators
5301 1 View Code Duplication
                        for ($i = 0; $i < $token['data']['args']; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5302 1
                            $ops[] = array_pop($formulaStrings);
5303
                        }
5304 1
                        $ops = array_reverse($ops);
5305 1
                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
5306 1
                        unset($space0, $space1);
5307
                    } else {
5308
                        // add-in function
5309
                        $ops = []; // array of operators
5310 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...
5311
                            $ops[] = array_pop($formulaStrings);
5312
                        }
5313
                        $ops = array_reverse($ops);
5314
                        $function = array_pop($formulaStrings);
5315
                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
5316
                        unset($space0, $space1);
5317
                    }
5318 1
                    break;
5319 2
                case 'tParen': // parenthesis
5320
                    $expression = array_pop($formulaStrings);
5321
                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
5322
                    unset($space2, $space3, $space4, $space5);
5323
                    break;
5324 2
                case 'tArray': // array constant
5325
                    $constantArray = self::readBIFF8ConstantArray($additionalData);
5326
                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
5327
                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
5328
                    unset($space0, $space1);
5329
                    break;
5330 2
                case 'tMemArea':
5331
                    // bite off chunk of additional data
5332
                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
5333
                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
5334
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5335
                    unset($space0, $space1);
5336
                    break;
5337 2
                case 'tArea': // cell range address
5338 2
                case 'tBool': // boolean
5339 2
                case 'tErr': // error code
5340 2
                case 'tInt': // integer
5341 2
                case 'tMemErr':
5342 2
                case 'tMemFunc':
5343 2
                case 'tMissArg':
5344 2
                case 'tName':
5345 2
                case 'tNameX':
5346 2
                case 'tNum': // number
5347 2
                case 'tRef': // single cell reference
5348 2
                case 'tRef3d': // 3d cell reference
5349 2
                case 'tArea3d': // 3d cell range reference
5350 1
                case 'tRefN':
5351 1
                case 'tAreaN':
5352 1
                case 'tStr': // string
5353 2
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5354 2
                    unset($space0, $space1);
5355 2
                    break;
5356
            }
5357
        }
5358 2
        $formulaString = $formulaStrings[0];
5359
5360 2
        return $formulaString;
5361
    }
5362
5363
    /**
5364
     * Fetch next token from binary formula data.
5365
     *
5366
     * @param string $formulaData Formula data
5367
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5368
     *
5369
     * @throws Exception
5370
     *
5371
     * @return array
5372
     */
5373 2
    private function getNextToken($formulaData, $baseCell = 'A1')
5374
    {
5375
        // offset: 0; size: 1; token id
5376 2
        $id = ord($formulaData[0]); // token id
5377 2
        $name = false; // initialize token name
0 ignored issues
show
Unused Code introduced by
$name is not used, you could remove the assignment.

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

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

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

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

Loading history...
5378
5379
        switch ($id) {
5380 2
            case 0x03:
5381 1
                $name = 'tAdd';
5382 1
                $size = 1;
5383 1
                $data = '+';
5384 1
                break;
5385 2
            case 0x04:
5386
                $name = 'tSub';
5387
                $size = 1;
5388
                $data = '-';
5389
                break;
5390 2
            case 0x05:
5391 2
                $name = 'tMul';
5392 2
                $size = 1;
5393 2
                $data = '*';
5394 2
                break;
5395 2
            case 0x06:
5396
                $name = 'tDiv';
5397
                $size = 1;
5398
                $data = '/';
5399
                break;
5400 2
            case 0x07:
5401
                $name = 'tPower';
5402
                $size = 1;
5403
                $data = '^';
5404
                break;
5405 2
            case 0x08:
5406
                $name = 'tConcat';
5407
                $size = 1;
5408
                $data = '&';
5409
                break;
5410 2
            case 0x09:
5411
                $name = 'tLT';
5412
                $size = 1;
5413
                $data = '<';
5414
                break;
5415 2
            case 0x0A:
5416
                $name = 'tLE';
5417
                $size = 1;
5418
                $data = '<=';
5419
                break;
5420 2
            case 0x0B:
5421
                $name = 'tEQ';
5422
                $size = 1;
5423
                $data = '=';
5424
                break;
5425 2
            case 0x0C:
5426
                $name = 'tGE';
5427
                $size = 1;
5428
                $data = '>=';
5429
                break;
5430 2
            case 0x0D:
5431
                $name = 'tGT';
5432
                $size = 1;
5433
                $data = '>';
5434
                break;
5435 2
            case 0x0E:
5436 1
                $name = 'tNE';
5437 1
                $size = 1;
5438 1
                $data = '<>';
5439 1
                break;
5440 2
            case 0x0F:
5441
                $name = 'tIsect';
5442
                $size = 1;
5443
                $data = ' ';
5444
                break;
5445 2
            case 0x10:
5446
                $name = 'tList';
5447
                $size = 1;
5448
                $data = ',';
5449
                break;
5450 2
            case 0x11:
5451
                $name = 'tRange';
5452
                $size = 1;
5453
                $data = ':';
5454
                break;
5455 2
            case 0x12:
5456
                $name = 'tUplus';
5457
                $size = 1;
5458
                $data = '+';
5459
                break;
5460 2
            case 0x13:
5461
                $name = 'tUminus';
5462
                $size = 1;
5463
                $data = '-';
5464
                break;
5465 2
            case 0x14:
5466
                $name = 'tPercent';
5467
                $size = 1;
5468
                $data = '%';
5469
                break;
5470 2
            case 0x15:    //    parenthesis
5471
                $name = 'tParen';
5472
                $size = 1;
5473
                $data = null;
5474
                break;
5475 2
            case 0x16:    //    missing argument
5476
                $name = 'tMissArg';
5477
                $size = 1;
5478
                $data = '';
5479
                break;
5480 2
            case 0x17:    //    string
5481 1
                $name = 'tStr';
5482
                // offset: 1; size: var; Unicode string, 8-bit string length
5483 1
                $string = self::readUnicodeStringShort(substr($formulaData, 1));
5484 1
                $size = 1 + $string['size'];
5485 1
                $data = self::UTF8toExcelDoubleQuoted($string['value']);
5486 1
                break;
5487 2
            case 0x19:    //    Special attribute
5488
                // offset: 1; size: 1; attribute type flags:
5489 1
                switch (ord($formulaData[1])) {
5490 1
                    case 0x01:
5491
                        $name = 'tAttrVolatile';
5492
                        $size = 4;
5493
                        $data = null;
5494
                        break;
5495 1
                    case 0x02:
5496
                        $name = 'tAttrIf';
5497
                        $size = 4;
5498
                        $data = null;
5499
                        break;
5500 1
                    case 0x04:
5501
                        $name = 'tAttrChoose';
5502
                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
5503
                        $nc = self::getInt2d($formulaData, 2);
5504
                        // offset: 4; size: 2 * $nc
5505
                        // offset: 4 + 2 * $nc; size: 2
5506
                        $size = 2 * $nc + 6;
5507
                        $data = null;
5508
                        break;
5509 1
                    case 0x08:
5510
                        $name = 'tAttrSkip';
5511
                        $size = 4;
5512
                        $data = null;
5513
                        break;
5514 1
                    case 0x10:
5515 1
                        $name = 'tAttrSum';
5516 1
                        $size = 4;
5517 1
                        $data = null;
5518 1
                        break;
5519
                    case 0x40:
5520
                    case 0x41:
5521
                        $name = 'tAttrSpace';
5522
                        $size = 4;
5523
                        // offset: 2; size: 2; space type and position
5524
                        switch (ord($formulaData[2])) {
5525
                            case 0x00:
5526
                                $spacetype = 'type0';
5527
                                break;
5528
                            case 0x01:
5529
                                $spacetype = 'type1';
5530
                                break;
5531
                            case 0x02:
5532
                                $spacetype = 'type2';
5533
                                break;
5534
                            case 0x03:
5535
                                $spacetype = 'type3';
5536
                                break;
5537
                            case 0x04:
5538
                                $spacetype = 'type4';
5539
                                break;
5540
                            case 0x05:
5541
                                $spacetype = 'type5';
5542
                                break;
5543
                            default:
5544
                                throw new Exception('Unrecognized space type in tAttrSpace token');
5545
                                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...
5546
                        }
5547
                        // offset: 3; size: 1; number of inserted spaces/carriage returns
5548
                        $spacecount = ord($formulaData[3]);
5549
5550
                        $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
5551
                        break;
5552
                    default:
5553
                        throw new Exception('Unrecognized attribute flag in tAttr token');
5554
                        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...
5555
                }
5556 1
                break;
5557 2
            case 0x1C:    //    error code
5558
                // offset: 1; size: 1; error code
5559
                $name = 'tErr';
5560
                $size = 2;
5561
                $data = Xls\ErrorCode::lookup(ord($formulaData[1]));
5562
                break;
5563 2
            case 0x1D:    //    boolean
5564
                // 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...
5565
                $name = 'tBool';
5566
                $size = 2;
5567
                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
5568
                break;
5569 2
            case 0x1E:    //    integer
5570
                // offset: 1; size: 2; unsigned 16-bit integer
5571
                $name = 'tInt';
5572
                $size = 3;
5573
                $data = self::getInt2d($formulaData, 1);
5574
                break;
5575 2
            case 0x1F:    //    number
5576
                // 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...
5577 2
                $name = 'tNum';
5578 2
                $size = 9;
5579 2
                $data = self::extractNumber(substr($formulaData, 1));
5580 2
                $data = str_replace(',', '.', (string) $data); // in case non-English locale
5581 2
                break;
5582 2
            case 0x20:    //    array constant
5583 2
            case 0x40:
5584 2
            case 0x60:
5585
                // offset: 1; size: 7; not used
5586
                $name = 'tArray';
5587
                $size = 8;
5588
                $data = null;
5589
                break;
5590 2
            case 0x21:    //    function with fixed number of arguments
5591 2
            case 0x41:
5592 2
            case 0x61:
5593
                $name = 'tFunc';
5594
                $size = 3;
5595
                // offset: 1; size: 2; index to built-in sheet function
5596
                switch (self::getInt2d($formulaData, 1)) {
5597
                    case 2:
5598
                        $function = 'ISNA';
5599
                        $args = 1;
5600
                        break;
5601
                    case 3:
5602
                        $function = 'ISERROR';
5603
                        $args = 1;
5604
                        break;
5605
                    case 10:
5606
                        $function = 'NA';
5607
                        $args = 0;
5608
                        break;
5609
                    case 15:
5610
                        $function = 'SIN';
5611
                        $args = 1;
5612
                        break;
5613
                    case 16:
5614
                        $function = 'COS';
5615
                        $args = 1;
5616
                        break;
5617
                    case 17:
5618
                        $function = 'TAN';
5619
                        $args = 1;
5620
                        break;
5621
                    case 18:
5622
                        $function = 'ATAN';
5623
                        $args = 1;
5624
                        break;
5625
                    case 19:
5626
                        $function = 'PI';
5627
                        $args = 0;
5628
                        break;
5629
                    case 20:
5630
                        $function = 'SQRT';
5631
                        $args = 1;
5632
                        break;
5633
                    case 21:
5634
                        $function = 'EXP';
5635
                        $args = 1;
5636
                        break;
5637
                    case 22:
5638
                        $function = 'LN';
5639
                        $args = 1;
5640
                        break;
5641
                    case 23:
5642
                        $function = 'LOG10';
5643
                        $args = 1;
5644
                        break;
5645
                    case 24:
5646
                        $function = 'ABS';
5647
                        $args = 1;
5648
                        break;
5649
                    case 25:
5650
                        $function = 'INT';
5651
                        $args = 1;
5652
                        break;
5653
                    case 26:
5654
                        $function = 'SIGN';
5655
                        $args = 1;
5656
                        break;
5657
                    case 27:
5658
                        $function = 'ROUND';
5659
                        $args = 2;
5660
                        break;
5661
                    case 30:
5662
                        $function = 'REPT';
5663
                        $args = 2;
5664
                        break;
5665
                    case 31:
5666
                        $function = 'MID';
5667
                        $args = 3;
5668
                        break;
5669
                    case 32:
5670
                        $function = 'LEN';
5671
                        $args = 1;
5672
                        break;
5673
                    case 33:
5674
                        $function = 'VALUE';
5675
                        $args = 1;
5676
                        break;
5677
                    case 34:
5678
                        $function = 'TRUE';
5679
                        $args = 0;
5680
                        break;
5681
                    case 35:
5682
                        $function = 'FALSE';
5683
                        $args = 0;
5684
                        break;
5685
                    case 38:
5686
                        $function = 'NOT';
5687
                        $args = 1;
5688
                        break;
5689
                    case 39:
5690
                        $function = 'MOD';
5691
                        $args = 2;
5692
                        break;
5693
                    case 40:
5694
                        $function = 'DCOUNT';
5695
                        $args = 3;
5696
                        break;
5697
                    case 41:
5698
                        $function = 'DSUM';
5699
                        $args = 3;
5700
                        break;
5701
                    case 42:
5702
                        $function = 'DAVERAGE';
5703
                        $args = 3;
5704
                        break;
5705
                    case 43:
5706
                        $function = 'DMIN';
5707
                        $args = 3;
5708
                        break;
5709
                    case 44:
5710
                        $function = 'DMAX';
5711
                        $args = 3;
5712
                        break;
5713
                    case 45:
5714
                        $function = 'DSTDEV';
5715
                        $args = 3;
5716
                        break;
5717
                    case 48:
5718
                        $function = 'TEXT';
5719
                        $args = 2;
5720
                        break;
5721
                    case 61:
5722
                        $function = 'MIRR';
5723
                        $args = 3;
5724
                        break;
5725
                    case 63:
5726
                        $function = 'RAND';
5727
                        $args = 0;
5728
                        break;
5729
                    case 65:
5730
                        $function = 'DATE';
5731
                        $args = 3;
5732
                        break;
5733
                    case 66:
5734
                        $function = 'TIME';
5735
                        $args = 3;
5736
                        break;
5737
                    case 67:
5738
                        $function = 'DAY';
5739
                        $args = 1;
5740
                        break;
5741
                    case 68:
5742
                        $function = 'MONTH';
5743
                        $args = 1;
5744
                        break;
5745
                    case 69:
5746
                        $function = 'YEAR';
5747
                        $args = 1;
5748
                        break;
5749
                    case 71:
5750
                        $function = 'HOUR';
5751
                        $args = 1;
5752
                        break;
5753
                    case 72:
5754
                        $function = 'MINUTE';
5755
                        $args = 1;
5756
                        break;
5757
                    case 73:
5758
                        $function = 'SECOND';
5759
                        $args = 1;
5760
                        break;
5761
                    case 74:
5762
                        $function = 'NOW';
5763
                        $args = 0;
5764
                        break;
5765
                    case 75:
5766
                        $function = 'AREAS';
5767
                        $args = 1;
5768
                        break;
5769
                    case 76:
5770
                        $function = 'ROWS';
5771
                        $args = 1;
5772
                        break;
5773
                    case 77:
5774
                        $function = 'COLUMNS';
5775
                        $args = 1;
5776
                        break;
5777
                    case 83:
5778
                        $function = 'TRANSPOSE';
5779
                        $args = 1;
5780
                        break;
5781
                    case 86:
5782
                        $function = 'TYPE';
5783
                        $args = 1;
5784
                        break;
5785
                    case 97:
5786
                        $function = 'ATAN2';
5787
                        $args = 2;
5788
                        break;
5789
                    case 98:
5790
                        $function = 'ASIN';
5791
                        $args = 1;
5792
                        break;
5793
                    case 99:
5794
                        $function = 'ACOS';
5795
                        $args = 1;
5796
                        break;
5797
                    case 105:
5798
                        $function = 'ISREF';
5799
                        $args = 1;
5800
                        break;
5801
                    case 111:
5802
                        $function = 'CHAR';
5803
                        $args = 1;
5804
                        break;
5805
                    case 112:
5806
                        $function = 'LOWER';
5807
                        $args = 1;
5808
                        break;
5809
                    case 113:
5810
                        $function = 'UPPER';
5811
                        $args = 1;
5812
                        break;
5813
                    case 114:
5814
                        $function = 'PROPER';
5815
                        $args = 1;
5816
                        break;
5817
                    case 117:
5818
                        $function = 'EXACT';
5819
                        $args = 2;
5820
                        break;
5821
                    case 118:
5822
                        $function = 'TRIM';
5823
                        $args = 1;
5824
                        break;
5825
                    case 119:
5826
                        $function = 'REPLACE';
5827
                        $args = 4;
5828
                        break;
5829
                    case 121:
5830
                        $function = 'CODE';
5831
                        $args = 1;
5832
                        break;
5833
                    case 126:
5834
                        $function = 'ISERR';
5835
                        $args = 1;
5836
                        break;
5837
                    case 127:
5838
                        $function = 'ISTEXT';
5839
                        $args = 1;
5840
                        break;
5841
                    case 128:
5842
                        $function = 'ISNUMBER';
5843
                        $args = 1;
5844
                        break;
5845
                    case 129:
5846
                        $function = 'ISBLANK';
5847
                        $args = 1;
5848
                        break;
5849
                    case 130:
5850
                        $function = 'T';
5851
                        $args = 1;
5852
                        break;
5853
                    case 131:
5854
                        $function = 'N';
5855
                        $args = 1;
5856
                        break;
5857
                    case 140:
5858
                        $function = 'DATEVALUE';
5859
                        $args = 1;
5860
                        break;
5861
                    case 141:
5862
                        $function = 'TIMEVALUE';
5863
                        $args = 1;
5864
                        break;
5865
                    case 142:
5866
                        $function = 'SLN';
5867
                        $args = 3;
5868
                        break;
5869
                    case 143:
5870
                        $function = 'SYD';
5871
                        $args = 4;
5872
                        break;
5873
                    case 162:
5874
                        $function = 'CLEAN';
5875
                        $args = 1;
5876
                        break;
5877
                    case 163:
5878
                        $function = 'MDETERM';
5879
                        $args = 1;
5880
                        break;
5881
                    case 164:
5882
                        $function = 'MINVERSE';
5883
                        $args = 1;
5884
                        break;
5885
                    case 165:
5886
                        $function = 'MMULT';
5887
                        $args = 2;
5888
                        break;
5889
                    case 184:
5890
                        $function = 'FACT';
5891
                        $args = 1;
5892
                        break;
5893
                    case 189:
5894
                        $function = 'DPRODUCT';
5895
                        $args = 3;
5896
                        break;
5897
                    case 190:
5898
                        $function = 'ISNONTEXT';
5899
                        $args = 1;
5900
                        break;
5901
                    case 195:
5902
                        $function = 'DSTDEVP';
5903
                        $args = 3;
5904
                        break;
5905
                    case 196:
5906
                        $function = 'DVARP';
5907
                        $args = 3;
5908
                        break;
5909
                    case 198:
5910
                        $function = 'ISLOGICAL';
5911
                        $args = 1;
5912
                        break;
5913
                    case 199:
5914
                        $function = 'DCOUNTA';
5915
                        $args = 3;
5916
                        break;
5917
                    case 207:
5918
                        $function = 'REPLACEB';
5919
                        $args = 4;
5920
                        break;
5921
                    case 210:
5922
                        $function = 'MIDB';
5923
                        $args = 3;
5924
                        break;
5925
                    case 211:
5926
                        $function = 'LENB';
5927
                        $args = 1;
5928
                        break;
5929
                    case 212:
5930
                        $function = 'ROUNDUP';
5931
                        $args = 2;
5932
                        break;
5933
                    case 213:
5934
                        $function = 'ROUNDDOWN';
5935
                        $args = 2;
5936
                        break;
5937
                    case 214:
5938
                        $function = 'ASC';
5939
                        $args = 1;
5940
                        break;
5941
                    case 215:
5942
                        $function = 'DBCS';
5943
                        $args = 1;
5944
                        break;
5945
                    case 221:
5946
                        $function = 'TODAY';
5947
                        $args = 0;
5948
                        break;
5949
                    case 229:
5950
                        $function = 'SINH';
5951
                        $args = 1;
5952
                        break;
5953
                    case 230:
5954
                        $function = 'COSH';
5955
                        $args = 1;
5956
                        break;
5957
                    case 231:
5958
                        $function = 'TANH';
5959
                        $args = 1;
5960
                        break;
5961
                    case 232:
5962
                        $function = 'ASINH';
5963
                        $args = 1;
5964
                        break;
5965
                    case 233:
5966
                        $function = 'ACOSH';
5967
                        $args = 1;
5968
                        break;
5969
                    case 234:
5970
                        $function = 'ATANH';
5971
                        $args = 1;
5972
                        break;
5973
                    case 235:
5974
                        $function = 'DGET';
5975
                        $args = 3;
5976
                        break;
5977
                    case 244:
5978
                        $function = 'INFO';
5979
                        $args = 1;
5980
                        break;
5981
                    case 252:
5982
                        $function = 'FREQUENCY';
5983
                        $args = 2;
5984
                        break;
5985
                    case 261:
5986
                        $function = 'ERROR.TYPE';
5987
                        $args = 1;
5988
                        break;
5989
                    case 271:
5990
                        $function = 'GAMMALN';
5991
                        $args = 1;
5992
                        break;
5993
                    case 273:
5994
                        $function = 'BINOMDIST';
5995
                        $args = 4;
5996
                        break;
5997
                    case 274:
5998
                        $function = 'CHIDIST';
5999
                        $args = 2;
6000
                        break;
6001
                    case 275:
6002
                        $function = 'CHIINV';
6003
                        $args = 2;
6004
                        break;
6005
                    case 276:
6006
                        $function = 'COMBIN';
6007
                        $args = 2;
6008
                        break;
6009
                    case 277:
6010
                        $function = 'CONFIDENCE';
6011
                        $args = 3;
6012
                        break;
6013
                    case 278:
6014
                        $function = 'CRITBINOM';
6015
                        $args = 3;
6016
                        break;
6017
                    case 279:
6018
                        $function = 'EVEN';
6019
                        $args = 1;
6020
                        break;
6021
                    case 280:
6022
                        $function = 'EXPONDIST';
6023
                        $args = 3;
6024
                        break;
6025
                    case 281:
6026
                        $function = 'FDIST';
6027
                        $args = 3;
6028
                        break;
6029
                    case 282:
6030
                        $function = 'FINV';
6031
                        $args = 3;
6032
                        break;
6033
                    case 283:
6034
                        $function = 'FISHER';
6035
                        $args = 1;
6036
                        break;
6037
                    case 284:
6038
                        $function = 'FISHERINV';
6039
                        $args = 1;
6040
                        break;
6041
                    case 285:
6042
                        $function = 'FLOOR';
6043
                        $args = 2;
6044
                        break;
6045
                    case 286:
6046
                        $function = 'GAMMADIST';
6047
                        $args = 4;
6048
                        break;
6049
                    case 287:
6050
                        $function = 'GAMMAINV';
6051
                        $args = 3;
6052
                        break;
6053
                    case 288:
6054
                        $function = 'CEILING';
6055
                        $args = 2;
6056
                        break;
6057
                    case 289:
6058
                        $function = 'HYPGEOMDIST';
6059
                        $args = 4;
6060
                        break;
6061
                    case 290:
6062
                        $function = 'LOGNORMDIST';
6063
                        $args = 3;
6064
                        break;
6065
                    case 291:
6066
                        $function = 'LOGINV';
6067
                        $args = 3;
6068
                        break;
6069
                    case 292:
6070
                        $function = 'NEGBINOMDIST';
6071
                        $args = 3;
6072
                        break;
6073
                    case 293:
6074
                        $function = 'NORMDIST';
6075
                        $args = 4;
6076
                        break;
6077
                    case 294:
6078
                        $function = 'NORMSDIST';
6079
                        $args = 1;
6080
                        break;
6081
                    case 295:
6082
                        $function = 'NORMINV';
6083
                        $args = 3;
6084
                        break;
6085
                    case 296:
6086
                        $function = 'NORMSINV';
6087
                        $args = 1;
6088
                        break;
6089
                    case 297:
6090
                        $function = 'STANDARDIZE';
6091
                        $args = 3;
6092
                        break;
6093
                    case 298:
6094
                        $function = 'ODD';
6095
                        $args = 1;
6096
                        break;
6097
                    case 299:
6098
                        $function = 'PERMUT';
6099
                        $args = 2;
6100
                        break;
6101
                    case 300:
6102
                        $function = 'POISSON';
6103
                        $args = 3;
6104
                        break;
6105
                    case 301:
6106
                        $function = 'TDIST';
6107
                        $args = 3;
6108
                        break;
6109
                    case 302:
6110
                        $function = 'WEIBULL';
6111
                        $args = 4;
6112
                        break;
6113
                    case 303:
6114
                        $function = 'SUMXMY2';
6115
                        $args = 2;
6116
                        break;
6117
                    case 304:
6118
                        $function = 'SUMX2MY2';
6119
                        $args = 2;
6120
                        break;
6121
                    case 305:
6122
                        $function = 'SUMX2PY2';
6123
                        $args = 2;
6124
                        break;
6125
                    case 306:
6126
                        $function = 'CHITEST';
6127
                        $args = 2;
6128
                        break;
6129
                    case 307:
6130
                        $function = 'CORREL';
6131
                        $args = 2;
6132
                        break;
6133
                    case 308:
6134
                        $function = 'COVAR';
6135
                        $args = 2;
6136
                        break;
6137
                    case 309:
6138
                        $function = 'FORECAST';
6139
                        $args = 3;
6140
                        break;
6141
                    case 310:
6142
                        $function = 'FTEST';
6143
                        $args = 2;
6144
                        break;
6145
                    case 311:
6146
                        $function = 'INTERCEPT';
6147
                        $args = 2;
6148
                        break;
6149
                    case 312:
6150
                        $function = 'PEARSON';
6151
                        $args = 2;
6152
                        break;
6153
                    case 313:
6154
                        $function = 'RSQ';
6155
                        $args = 2;
6156
                        break;
6157
                    case 314:
6158
                        $function = 'STEYX';
6159
                        $args = 2;
6160
                        break;
6161
                    case 315:
6162
                        $function = 'SLOPE';
6163
                        $args = 2;
6164
                        break;
6165
                    case 316:
6166
                        $function = 'TTEST';
6167
                        $args = 4;
6168
                        break;
6169
                    case 325:
6170
                        $function = 'LARGE';
6171
                        $args = 2;
6172
                        break;
6173
                    case 326:
6174
                        $function = 'SMALL';
6175
                        $args = 2;
6176
                        break;
6177
                    case 327:
6178
                        $function = 'QUARTILE';
6179
                        $args = 2;
6180
                        break;
6181
                    case 328:
6182
                        $function = 'PERCENTILE';
6183
                        $args = 2;
6184
                        break;
6185
                    case 331:
6186
                        $function = 'TRIMMEAN';
6187
                        $args = 2;
6188
                        break;
6189
                    case 332:
6190
                        $function = 'TINV';
6191
                        $args = 2;
6192
                        break;
6193
                    case 337:
6194
                        $function = 'POWER';
6195
                        $args = 2;
6196
                        break;
6197
                    case 342:
6198
                        $function = 'RADIANS';
6199
                        $args = 1;
6200
                        break;
6201
                    case 343:
6202
                        $function = 'DEGREES';
6203
                        $args = 1;
6204
                        break;
6205
                    case 346:
6206
                        $function = 'COUNTIF';
6207
                        $args = 2;
6208
                        break;
6209
                    case 347:
6210
                        $function = 'COUNTBLANK';
6211
                        $args = 1;
6212
                        break;
6213
                    case 350:
6214
                        $function = 'ISPMT';
6215
                        $args = 4;
6216
                        break;
6217
                    case 351:
6218
                        $function = 'DATEDIF';
6219
                        $args = 3;
6220
                        break;
6221
                    case 352:
6222
                        $function = 'DATESTRING';
6223
                        $args = 1;
6224
                        break;
6225
                    case 353:
6226
                        $function = 'NUMBERSTRING';
6227
                        $args = 2;
6228
                        break;
6229
                    case 360:
6230
                        $function = 'PHONETIC';
6231
                        $args = 1;
6232
                        break;
6233
                    case 368:
6234
                        $function = 'BAHTTEXT';
6235
                        $args = 1;
6236
                        break;
6237
                    default:
6238
                        throw new Exception('Unrecognized function in formula');
6239
                        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...
6240
                }
6241
                $data = ['function' => $function, 'args' => $args];
6242
                break;
6243 2
            case 0x22:    //    function with variable number of arguments
6244 2
            case 0x42:
6245 2
            case 0x62:
6246 1
                $name = 'tFuncV';
6247 1
                $size = 4;
6248
                // offset: 1; size: 1; number of arguments
6249 1
                $args = ord($formulaData[1]);
6250
                // offset: 2: size: 2; index to built-in sheet function
6251 1
                $index = self::getInt2d($formulaData, 2);
6252
                switch ($index) {
6253 1
                    case 0:
6254
                        $function = 'COUNT';
6255
                        break;
6256 1
                    case 1:
6257 1
                        $function = 'IF';
6258 1
                        break;
6259 1
                    case 4:
6260 1
                        $function = 'SUM';
6261 1
                        break;
6262
                    case 5:
6263
                        $function = 'AVERAGE';
6264
                        break;
6265
                    case 6:
6266
                        $function = 'MIN';
6267
                        break;
6268
                    case 7:
6269
                        $function = 'MAX';
6270
                        break;
6271
                    case 8:
6272
                        $function = 'ROW';
6273
                        break;
6274
                    case 9:
6275
                        $function = 'COLUMN';
6276
                        break;
6277
                    case 11:
6278
                        $function = 'NPV';
6279
                        break;
6280
                    case 12:
6281
                        $function = 'STDEV';
6282
                        break;
6283
                    case 13:
6284
                        $function = 'DOLLAR';
6285
                        break;
6286
                    case 14:
6287
                        $function = 'FIXED';
6288
                        break;
6289
                    case 28:
6290
                        $function = 'LOOKUP';
6291
                        break;
6292
                    case 29:
6293
                        $function = 'INDEX';
6294
                        break;
6295
                    case 36:
6296
                        $function = 'AND';
6297
                        break;
6298
                    case 37:
6299
                        $function = 'OR';
6300
                        break;
6301
                    case 46:
6302
                        $function = 'VAR';
6303
                        break;
6304
                    case 49:
6305
                        $function = 'LINEST';
6306
                        break;
6307
                    case 50:
6308
                        $function = 'TREND';
6309
                        break;
6310
                    case 51:
6311
                        $function = 'LOGEST';
6312
                        break;
6313
                    case 52:
6314
                        $function = 'GROWTH';
6315
                        break;
6316
                    case 56:
6317
                        $function = 'PV';
6318
                        break;
6319
                    case 57:
6320
                        $function = 'FV';
6321
                        break;
6322
                    case 58:
6323
                        $function = 'NPER';
6324
                        break;
6325
                    case 59:
6326
                        $function = 'PMT';
6327
                        break;
6328
                    case 60:
6329
                        $function = 'RATE';
6330
                        break;
6331
                    case 62:
6332
                        $function = 'IRR';
6333
                        break;
6334
                    case 64:
6335
                        $function = 'MATCH';
6336
                        break;
6337
                    case 70:
6338
                        $function = 'WEEKDAY';
6339
                        break;
6340
                    case 78:
6341
                        $function = 'OFFSET';
6342
                        break;
6343
                    case 82:
6344
                        $function = 'SEARCH';
6345
                        break;
6346
                    case 100:
6347
                        $function = 'CHOOSE';
6348
                        break;
6349
                    case 101:
6350
                        $function = 'HLOOKUP';
6351
                        break;
6352
                    case 102:
6353
                        $function = 'VLOOKUP';
6354
                        break;
6355
                    case 109:
6356
                        $function = 'LOG';
6357
                        break;
6358
                    case 115:
6359
                        $function = 'LEFT';
6360
                        break;
6361
                    case 116:
6362
                        $function = 'RIGHT';
6363
                        break;
6364
                    case 120:
6365
                        $function = 'SUBSTITUTE';
6366
                        break;
6367
                    case 124:
6368
                        $function = 'FIND';
6369
                        break;
6370
                    case 125:
6371
                        $function = 'CELL';
6372
                        break;
6373
                    case 144:
6374
                        $function = 'DDB';
6375
                        break;
6376
                    case 148:
6377
                        $function = 'INDIRECT';
6378
                        break;
6379
                    case 167:
6380
                        $function = 'IPMT';
6381
                        break;
6382
                    case 168:
6383
                        $function = 'PPMT';
6384
                        break;
6385
                    case 169:
6386
                        $function = 'COUNTA';
6387
                        break;
6388
                    case 183:
6389
                        $function = 'PRODUCT';
6390
                        break;
6391
                    case 193:
6392
                        $function = 'STDEVP';
6393
                        break;
6394
                    case 194:
6395
                        $function = 'VARP';
6396
                        break;
6397
                    case 197:
6398
                        $function = 'TRUNC';
6399
                        break;
6400
                    case 204:
6401
                        $function = 'USDOLLAR';
6402
                        break;
6403
                    case 205:
6404
                        $function = 'FINDB';
6405
                        break;
6406
                    case 206:
6407
                        $function = 'SEARCHB';
6408
                        break;
6409
                    case 208:
6410
                        $function = 'LEFTB';
6411
                        break;
6412
                    case 209:
6413
                        $function = 'RIGHTB';
6414
                        break;
6415
                    case 216:
6416
                        $function = 'RANK';
6417
                        break;
6418
                    case 219:
6419
                        $function = 'ADDRESS';
6420
                        break;
6421
                    case 220:
6422
                        $function = 'DAYS360';
6423
                        break;
6424
                    case 222:
6425
                        $function = 'VDB';
6426
                        break;
6427
                    case 227:
6428
                        $function = 'MEDIAN';
6429
                        break;
6430
                    case 228:
6431
                        $function = 'SUMPRODUCT';
6432
                        break;
6433
                    case 247:
6434
                        $function = 'DB';
6435
                        break;
6436
                    case 255:
6437
                        $function = '';
6438
                        break;
6439
                    case 269:
6440
                        $function = 'AVEDEV';
6441
                        break;
6442
                    case 270:
6443
                        $function = 'BETADIST';
6444
                        break;
6445
                    case 272:
6446
                        $function = 'BETAINV';
6447
                        break;
6448
                    case 317:
6449
                        $function = 'PROB';
6450
                        break;
6451
                    case 318:
6452
                        $function = 'DEVSQ';
6453
                        break;
6454
                    case 319:
6455
                        $function = 'GEOMEAN';
6456
                        break;
6457
                    case 320:
6458
                        $function = 'HARMEAN';
6459
                        break;
6460
                    case 321:
6461
                        $function = 'SUMSQ';
6462
                        break;
6463
                    case 322:
6464
                        $function = 'KURT';
6465
                        break;
6466
                    case 323:
6467
                        $function = 'SKEW';
6468
                        break;
6469
                    case 324:
6470
                        $function = 'ZTEST';
6471
                        break;
6472
                    case 329:
6473
                        $function = 'PERCENTRANK';
6474
                        break;
6475
                    case 330:
6476
                        $function = 'MODE';
6477
                        break;
6478
                    case 336:
6479
                        $function = 'CONCATENATE';
6480
                        break;
6481
                    case 344:
6482
                        $function = 'SUBTOTAL';
6483
                        break;
6484
                    case 345:
6485
                        $function = 'SUMIF';
6486
                        break;
6487
                    case 354:
6488
                        $function = 'ROMAN';
6489
                        break;
6490
                    case 358:
6491
                        $function = 'GETPIVOTDATA';
6492
                        break;
6493
                    case 359:
6494
                        $function = 'HYPERLINK';
6495
                        break;
6496
                    case 361:
6497
                        $function = 'AVERAGEA';
6498
                        break;
6499
                    case 362:
6500
                        $function = 'MAXA';
6501
                        break;
6502
                    case 363:
6503
                        $function = 'MINA';
6504
                        break;
6505
                    case 364:
6506
                        $function = 'STDEVPA';
6507
                        break;
6508
                    case 365:
6509
                        $function = 'VARPA';
6510
                        break;
6511
                    case 366:
6512
                        $function = 'STDEVA';
6513
                        break;
6514
                    case 367:
6515
                        $function = 'VARA';
6516
                        break;
6517
                    default:
6518
                        throw new Exception('Unrecognized function in formula');
6519
                        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...
6520
                }
6521 1
                $data = ['function' => $function, 'args' => $args];
6522 1
                break;
6523 2
            case 0x23:    //    index to defined name
6524 2
            case 0x43:
6525 2
            case 0x63:
6526
                $name = 'tName';
6527
                $size = 5;
6528
                // offset: 1; size: 2; one-based index to definedname record
6529
                $definedNameIndex = self::getInt2d($formulaData, 1) - 1;
6530
                // offset: 2; size: 2; not used
6531
                $data = $this->definedname[$definedNameIndex]['name'];
6532
                break;
6533 2
            case 0x24:    //    single cell reference e.g. A5
6534 2
            case 0x44:
6535 2
            case 0x64:
6536 2
                $name = 'tRef';
6537 2
                $size = 5;
6538 2
                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
6539 2
                break;
6540 2
            case 0x25:    //    cell range reference to cells in the same sheet (2d)
6541 1
            case 0x45:
6542 1
            case 0x65:
6543 2
                $name = 'tArea';
6544 2
                $size = 9;
6545 2
                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
6546 2
                break;
6547 1
            case 0x26:    //    Constant reference sub-expression
6548 1
            case 0x46:
6549 1 View Code Duplication
            case 0x66:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6550
                $name = 'tMemArea';
6551
                // offset: 1; size: 4; not used
6552
                // offset: 5; size: 2; size of the following subexpression
6553
                $subSize = self::getInt2d($formulaData, 5);
6554
                $size = 7 + $subSize;
6555
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6556
                break;
6557 1
            case 0x27:    //    Deleted constant reference sub-expression
6558 1
            case 0x47:
6559 1 View Code Duplication
            case 0x67:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6560
                $name = 'tMemErr';
6561
                // offset: 1; size: 4; not used
6562
                // offset: 5; size: 2; size of the following subexpression
6563
                $subSize = self::getInt2d($formulaData, 5);
6564
                $size = 7 + $subSize;
6565
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6566
                break;
6567 1
            case 0x29:    //    Variable reference sub-expression
6568 1
            case 0x49:
6569 1 View Code Duplication
            case 0x69:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6570
                $name = 'tMemFunc';
6571
                // offset: 1; size: 2; size of the following sub-expression
6572
                $subSize = self::getInt2d($formulaData, 1);
6573
                $size = 3 + $subSize;
6574
                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
6575
                break;
6576 1
            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
6577 1
            case 0x4C:
6578 1
            case 0x6C:
6579
                $name = 'tRefN';
6580
                $size = 5;
6581
                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
6582
                break;
6583 1
            case 0x2D:    //    Relative 2d range reference
6584 1
            case 0x4D:
6585 1
            case 0x6D:
6586
                $name = 'tAreaN';
6587
                $size = 9;
6588
                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
6589
                break;
6590 1
            case 0x39:    //    External name
6591 1
            case 0x59:
6592 1
            case 0x79:
6593
                $name = 'tNameX';
6594
                $size = 7;
6595
                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
6596
                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
6597
                $index = self::getInt2d($formulaData, 3);
6598
                // assume index is to EXTERNNAME record
6599
                $data = $this->externalNames[$index - 1]['name'];
6600
                // offset: 5; size: 2; not used
6601
                break;
6602 1
            case 0x3A:    //    3d reference to cell
6603 1
            case 0x5A:
6604 1 View Code Duplication
            case 0x7A:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6605
                $name = 'tRef3d';
6606
                $size = 7;
6607
6608
                try {
6609
                    // offset: 1; size: 2; index to REF entry
6610
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6611
                    // offset: 3; size: 4; cell address
6612
                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
6613
6614
                    $data = "$sheetRange!$cellAddress";
6615
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
6616
                    // deleted sheet reference
6617
                    $data = '#REF!';
6618
                }
6619
                break;
6620 1
            case 0x3B:    //    3d reference to cell range
6621
            case 0x5B:
6622 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...
6623 1
                $name = 'tArea3d';
6624 1
                $size = 11;
6625
6626
                try {
6627
                    // offset: 1; size: 2; index to REF entry
6628 1
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6629
                    // offset: 3; size: 8; cell address
6630 1
                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
6631
6632 1
                    $data = "$sheetRange!$cellRangeAddress";
6633
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
6634
                    // deleted sheet reference
6635
                    $data = '#REF!';
6636
                }
6637 1
                break;
6638
            // Unknown cases    // don't know how to deal with
6639
            default:
6640
                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
6641
                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...
6642
        }
6643
6644
        return [
6645 2
            'id' => $id,
6646 2
            'name' => $name,
6647 2
            'size' => $size,
6648 2
            'data' => $data,
6649
        ];
6650
    }
6651
6652
    /**
6653
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
6654
     * section 3.3.4.
6655
     *
6656
     * @param string $cellAddressStructure
6657
     *
6658
     * @return string
6659
     */
6660 2
    private function readBIFF8CellAddress($cellAddressStructure)
6661
    {
6662
        // 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...
6663 2
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6664
6665
        // offset: 2; size: 2; index to column or column offset + relative flags
6666
        // bit: 7-0; mask 0x00FF; column index
6667 2
        $column = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($cellAddressStructure, 2));
6668
6669
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6670 2
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
6671 1
            $column = '$' . $column;
6672
        }
6673
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6674 2
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6675 1
            $row = '$' . $row;
6676
        }
6677
6678 2
        return $column . $row;
6679
    }
6680
6681
    /**
6682
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6683
     * to indicate offsets from a base cell
6684
     * section 3.3.4.
6685
     *
6686
     * @param string $cellAddressStructure
6687
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
6688
     *
6689
     * @return string
6690
     */
6691
    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
6692
    {
6693
        list($baseCol, $baseRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6694
        $baseCol = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6695
6696
        // 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...
6697
        $rowIndex = self::getInt2d($cellAddressStructure, 0);
6698
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6699
6700
        // offset: 2; size: 2; index to column or column offset + relative flags
6701
        // bit: 7-0; mask 0x00FF; column index
6702
        $colIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
6703
6704
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6705 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...
6706
            $column = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colIndex);
6707
            $column = '$' . $column;
6708
        } else {
6709
            $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
6710
            $column = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $colIndex);
6711
        }
6712
6713
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6714
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6715
            $row = '$' . $row;
6716
        } else {
6717
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
6718
            $row = $baseRow + $rowIndex;
6719
        }
6720
6721
        return $column . $row;
6722
    }
6723
6724
    /**
6725
     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
6726
     * always fixed range
6727
     * section 2.5.14.
6728
     *
6729
     * @param string $subData
6730
     *
6731
     * @throws Exception
6732
     *
6733
     * @return string
6734
     */
6735 4
    private function readBIFF5CellRangeAddressFixed($subData)
6736
    {
6737
        // offset: 0; size: 2; index to first row
6738 4
        $fr = self::getInt2d($subData, 0) + 1;
6739
6740
        // offset: 2; size: 2; index to last row
6741 4
        $lr = self::getInt2d($subData, 2) + 1;
6742
6743
        // offset: 4; size: 1; index to first column
6744 4
        $fc = ord($subData[4]);
6745
6746
        // offset: 5; size: 1; index to last column
6747 4
        $lc = ord($subData[5]);
6748
6749
        // check values
6750 4
        if ($fr > $lr || $fc > $lc) {
6751
            throw new Exception('Not a cell range address');
6752
        }
6753
6754
        // column index to letter
6755 4
        $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6756 4
        $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6757
6758 4
        if ($fr == $lr and $fc == $lc) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
6759 4
            return "$fc$fr";
6760
        }
6761
6762 1
        return "$fc$fr:$lc$lr";
6763
    }
6764
6765
    /**
6766
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
6767
     * always fixed range
6768
     * section 2.5.14.
6769
     *
6770
     * @param string $subData
6771
     *
6772
     * @throws Exception
6773
     *
6774
     * @return string
6775
     */
6776 2
    private function readBIFF8CellRangeAddressFixed($subData)
6777
    {
6778
        // offset: 0; size: 2; index to first row
6779 2
        $fr = self::getInt2d($subData, 0) + 1;
6780
6781
        // offset: 2; size: 2; index to last row
6782 2
        $lr = self::getInt2d($subData, 2) + 1;
6783
6784
        // offset: 4; size: 2; index to first column
6785 2
        $fc = self::getInt2d($subData, 4);
6786
6787
        // offset: 6; size: 2; index to last column
6788 2
        $lc = self::getInt2d($subData, 6);
6789
6790
        // check values
6791 2
        if ($fr > $lr || $fc > $lc) {
6792
            throw new Exception('Not a cell range address');
6793
        }
6794
6795
        // column index to letter
6796 2
        $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6797 2
        $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6798
6799 2
        if ($fr == $lr and $fc == $lc) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
6800 2
            return "$fc$fr";
6801
        }
6802
6803 2
        return "$fc$fr:$lc$lr";
6804
    }
6805
6806
    /**
6807
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
6808
     * there are flags indicating whether column/row index is relative
6809
     * section 3.3.4.
6810
     *
6811
     * @param string $subData
6812
     *
6813
     * @return string
6814
     */
6815 2
    private function readBIFF8CellRangeAddress($subData)
6816
    {
6817
        // todo: if cell range is just a single cell, should this funciton
6818
        // not just return e.g. 'A1' and not 'A1:A1' ?
6819
6820
        // 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...
6821 2
            $fr = self::getInt2d($subData, 0) + 1;
6822
6823
        // 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...
6824 2
            $lr = self::getInt2d($subData, 2) + 1;
6825
6826
        // offset: 4; size: 2; index to first column or column offset + relative flags
6827
6828
        // bit: 7-0; mask 0x00FF; column index
6829 2
        $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 4));
6830
6831
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6832 2
        if (!(0x4000 & self::getInt2d($subData, 4))) {
6833 1
            $fc = '$' . $fc;
6834
        }
6835
6836
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6837 2
        if (!(0x8000 & self::getInt2d($subData, 4))) {
6838 1
            $fr = '$' . $fr;
6839
        }
6840
6841
        // offset: 6; size: 2; index to last column or column offset + relative flags
6842
6843
        // bit: 7-0; mask 0x00FF; column index
6844 2
        $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 6));
6845
6846
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6847 2
        if (!(0x4000 & self::getInt2d($subData, 6))) {
6848 1
            $lc = '$' . $lc;
6849
        }
6850
6851
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6852 2
        if (!(0x8000 & self::getInt2d($subData, 6))) {
6853 1
            $lr = '$' . $lr;
6854
        }
6855
6856 2
        return "$fc$fr:$lc$lr";
6857
    }
6858
6859
    /**
6860
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6861
     * to indicate offsets from a base cell
6862
     * section 3.3.4.
6863
     *
6864
     * @param string $subData
6865
     * @param string $baseCell Base cell
6866
     *
6867
     * @return string Cell range address
6868
     */
6869
    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
6870
    {
6871
        list($baseCol, $baseRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6872
        $baseCol = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6873
6874
        // TODO: if cell range is just a single cell, should this funciton
6875
        // not just return e.g. 'A1' and not 'A1:A1' ?
6876
6877
        // offset: 0; size: 2; first row
6878
        $frIndex = self::getInt2d($subData, 0); // adjust below
6879
6880
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
6881
        $lrIndex = self::getInt2d($subData, 2); // adjust below
6882
6883
        // offset: 4; size: 2; first column with relative/absolute flags
6884
6885
        // bit: 7-0; mask 0x00FF; column index
6886
        $fcIndex = 0x00FF & self::getInt2d($subData, 4);
6887
6888
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6889 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...
6890
            // absolute column index
6891
            $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fcIndex);
6892
            $fc = '$' . $fc;
6893
        } else {
6894
            // column offset
6895
            $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
6896
            $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $fcIndex);
6897
        }
6898
6899
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6900 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...
6901
            // absolute row index
6902
            $fr = $frIndex + 1;
6903
            $fr = '$' . $fr;
6904
        } else {
6905
            // row offset
6906
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
6907
            $fr = $baseRow + $frIndex;
6908
        }
6909
6910
        // offset: 6; size: 2; last column with relative/absolute flags
6911
6912
        // bit: 7-0; mask 0x00FF; column index
6913
        $lcIndex = 0x00FF & self::getInt2d($subData, 6);
6914
        $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6915
        $lc = \PhpOffice\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...
6916
6917
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6918 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...
6919
            // absolute column index
6920
            $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lcIndex);
6921
            $lc = '$' . $lc;
6922
        } else {
6923
            // column offset
6924
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6925
            $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
6926
        }
6927
6928
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6929 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...
6930
            // absolute row index
6931
            $lr = $lrIndex + 1;
6932
            $lr = '$' . $lr;
6933
        } else {
6934
            // row offset
6935
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
6936
            $lr = $baseRow + $lrIndex;
6937
        }
6938
6939
        return "$fc$fr:$lc$lr";
6940
    }
6941
6942
    /**
6943
     * Read BIFF8 cell range address list
6944
     * section 2.5.15.
6945
     *
6946
     * @param string $subData
6947
     *
6948
     * @return array
6949
     */
6950 2 View Code Duplication
    private function readBIFF8CellRangeAddressList($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
6951
    {
6952 2
        $cellRangeAddresses = [];
6953
6954
        // offset: 0; size: 2; number of the following cell range addresses
6955 2
        $nm = self::getInt2d($subData, 0);
6956
6957 2
        $offset = 2;
6958
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
6959 2
        for ($i = 0; $i < $nm; ++$i) {
6960 2
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
6961 2
            $offset += 8;
6962
        }
6963
6964
        return [
6965 2
            'size' => 2 + 8 * $nm,
6966 2
            'cellRangeAddresses' => $cellRangeAddresses,
6967
        ];
6968
    }
6969
6970
    /**
6971
     * Read BIFF5 cell range address list
6972
     * section 2.5.15.
6973
     *
6974
     * @param string $subData
6975
     *
6976
     * @return array
6977
     */
6978 4 View Code Duplication
    private function readBIFF5CellRangeAddressList($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
6979
    {
6980 4
        $cellRangeAddresses = [];
6981
6982
        // offset: 0; size: 2; number of the following cell range addresses
6983 4
        $nm = self::getInt2d($subData, 0);
6984
6985 4
        $offset = 2;
6986
        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
6987 4
        for ($i = 0; $i < $nm; ++$i) {
6988 4
            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
6989 4
            $offset += 6;
6990
        }
6991
6992
        return [
6993 4
            'size' => 2 + 6 * $nm,
6994 4
            'cellRangeAddresses' => $cellRangeAddresses,
6995
        ];
6996
    }
6997
6998
    /**
6999
     * Get a sheet range like Sheet1:Sheet3 from REF index
7000
     * Note: If there is only one sheet in the range, one gets e.g Sheet1
7001
     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
7002
     * in which case an Exception is thrown.
7003
     *
7004
     * @param int $index
7005
     *
7006
     * @throws Exception
7007
     *
7008
     * @return string|false
7009
     */
7010 1
    private function readSheetRangeByRefIndex($index)
7011
    {
7012 1
        if (isset($this->ref[$index])) {
7013 1
            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
7014
7015
            switch ($type) {
7016 1
                case 'internal':
7017
                    // check if we have a deleted 3d reference
7018 1
                    if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF or $this->ref[$index]['lastSheetIndex'] == 0xFFFF) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
7019
                        throw new Exception('Deleted sheet reference');
7020
                    }
7021
7022
                    // we have normal sheet range (collapsed or uncollapsed)
7023 1
                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
7024 1
                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
7025
7026 1
                    if ($firstSheetName == $lastSheetName) {
7027
                        // collapsed sheet range
7028 1
                        $sheetRange = $firstSheetName;
7029
                    } else {
7030
                        $sheetRange = "$firstSheetName:$lastSheetName";
7031
                    }
7032
7033
                    // escape the single-quotes
7034 1
                    $sheetRange = str_replace("'", "''", $sheetRange);
7035
7036
                    // if there are special characters, we need to enclose the range in single-quotes
7037
                    // todo: check if we have identified the whole set of special characters
7038
                    // it seems that the following characters are not accepted for sheet names
7039
                    // and we may assume that they are not present: []*/:\?
7040 1
                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
7041
                        $sheetRange = "'$sheetRange'";
7042
                    }
7043
7044 1
                    return $sheetRange;
7045
                    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...
7046
                default:
7047
                    // TODO: external sheet support
7048
                    throw new Exception('Xls reader only supports internal sheets in fomulas');
7049
                    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...
7050
            }
7051
        }
7052
7053
        return false;
7054
    }
7055
7056
    /**
7057
     * read BIFF8 constant value array from array data
7058
     * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
7059
     * section 2.5.8.
7060
     *
7061
     * @param string $arrayData
7062
     *
7063
     * @return array
7064
     */
7065
    private static function readBIFF8ConstantArray($arrayData)
7066
    {
7067
        // offset: 0; size: 1; number of columns decreased by 1
7068
        $nc = ord($arrayData[0]);
7069
7070
        // offset: 1; size: 2; number of rows decreased by 1
7071
        $nr = self::getInt2d($arrayData, 1);
7072
        $size = 3; // initialize
7073
        $arrayData = substr($arrayData, 3);
7074
7075
        // 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...
7076
        $matrixChunks = [];
7077
        for ($r = 1; $r <= $nr + 1; ++$r) {
7078
            $items = [];
7079
            for ($c = 1; $c <= $nc + 1; ++$c) {
7080
                $constant = self::readBIFF8Constant($arrayData);
7081
                $items[] = $constant['value'];
7082
                $arrayData = substr($arrayData, $constant['size']);
7083
                $size += $constant['size'];
7084
            }
7085
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
7086
        }
7087
        $matrix = '{' . implode(';', $matrixChunks) . '}';
7088
7089
        return [
7090
            'value' => $matrix,
7091
            'size' => $size,
7092
        ];
7093
    }
7094
7095
    /**
7096
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
7097
     * section 2.5.7
7098
     * returns e.g. array('value' => '5', 'size' => 9).
7099
     *
7100
     * @param string $valueData
7101
     *
7102
     * @return array
7103
     */
7104
    private static function readBIFF8Constant($valueData)
7105
    {
7106
        // offset: 0; size: 1; identifier for type of constant
7107
        $identifier = ord($valueData[0]);
7108
7109
        switch ($identifier) {
7110
            case 0x00: // empty constant (what is this?)
7111
                $value = '';
7112
                $size = 9;
7113
                break;
7114
            case 0x01: // number
7115
                // offset: 1; size: 8; IEEE 754 floating-point value
7116
                $value = self::extractNumber(substr($valueData, 1, 8));
7117
                $size = 9;
7118
                break;
7119
            case 0x02: // string value
7120
                // offset: 1; size: var; Unicode string, 16-bit string length
7121
                $string = self::readUnicodeStringLong(substr($valueData, 1));
7122
                $value = '"' . $string['value'] . '"';
7123
                $size = 1 + $string['size'];
7124
                break;
7125
            case 0x04: // boolean
7126
                // 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...
7127
                if (ord($valueData[1])) {
7128
                    $value = 'TRUE';
7129
                } else {
7130
                    $value = 'FALSE';
7131
                }
7132
                $size = 9;
7133
                break;
7134
            case 0x10: // error code
7135
                // offset: 1; size: 1; error code
7136
                $value = Xls\ErrorCode::lookup(ord($valueData[1]));
7137
                $size = 9;
7138
                break;
7139
        }
7140
7141
        return [
7142
            '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...
7143
            '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...
7144
        ];
7145
    }
7146
7147
    /**
7148
     * Extract RGB color
7149
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
7150
     *
7151
     * @param string $rgb Encoded RGB value (4 bytes)
7152
     *
7153
     * @return array
7154
     */
7155 2
    private static function readRGB($rgb)
7156
    {
7157
        // offset: 0; size 1; Red component
7158 2
        $r = ord($rgb[0]);
7159
7160
        // offset: 1; size: 1; Green component
7161 2
        $g = ord($rgb[1]);
7162
7163
        // offset: 2; size: 1; Blue component
7164 2
        $b = ord($rgb[2]);
7165
7166
        // HEX notation, e.g. 'FF00FC'
7167 2
        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
7168
7169 2
        return ['rgb' => $rgb];
7170
    }
7171
7172
    /**
7173
     * Read byte string (8-bit string length)
7174
     * OpenOffice documentation: 2.5.2.
7175
     *
7176
     * @param string $subData
7177
     *
7178
     * @return array
7179
     */
7180 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...
7181
    {
7182
        // offset: 0; size: 1; length of the string (character count)
7183
        $ln = ord($subData[0]);
7184
7185
        // 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...
7186
        $value = $this->decodeCodepage(substr($subData, 1, $ln));
7187
7188
        return [
7189
            'value' => $value,
7190
            'size' => 1 + $ln, // size in bytes of data structure
7191
        ];
7192
    }
7193
7194
    /**
7195
     * Read byte string (16-bit string length)
7196
     * OpenOffice documentation: 2.5.2.
7197
     *
7198
     * @param string $subData
7199
     *
7200
     * @return array
7201
     */
7202 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...
7203
    {
7204
        // offset: 0; size: 2; length of the string (character count)
7205
        $ln = self::getInt2d($subData, 0);
7206
7207
        // 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...
7208
        $value = $this->decodeCodepage(substr($subData, 2));
7209
7210
        //return $string;
7211
        return [
7212
            'value' => $value,
7213
            'size' => 2 + $ln, // size in bytes of data structure
7214
        ];
7215
    }
7216
7217
    /**
7218
     * Extracts an Excel Unicode short string (8-bit string length)
7219
     * OpenOffice documentation: 2.5.3
7220
     * function will automatically find out where the Unicode string ends.
7221
     *
7222
     * @param string $subData
7223
     *
7224
     * @return array
7225
     */
7226 4 View Code Duplication
    private static function readUnicodeStringShort($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
7227
    {
7228 4
        $value = '';
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

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

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

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

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

Loading history...
7229
7230
        // offset: 0: size: 1; length of the string (character count)
7231 4
        $characterCount = ord($subData[0]);
7232
7233 4
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7234
7235
        // add 1 for the string length
7236 4
        $string['size'] += 1;
7237
7238 4
        return $string;
7239
    }
7240
7241
    /**
7242
     * Extracts an Excel Unicode long string (16-bit string length)
7243
     * OpenOffice documentation: 2.5.3
7244
     * this function is under construction, needs to support rich text, and Asian phonetic settings.
7245
     *
7246
     * @param string $subData
7247
     *
7248
     * @return array
7249
     */
7250 4 View Code Duplication
    private static function readUnicodeStringLong($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
7251
    {
7252 4
        $value = '';
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

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

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

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

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

Loading history...
7253
7254
        // offset: 0: size: 2; length of the string (character count)
7255 4
        $characterCount = self::getInt2d($subData, 0);
7256
7257 4
        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7258
7259
        // add 2 for the string length
7260 4
        $string['size'] += 2;
7261
7262 4
        return $string;
7263
    }
7264
7265
    /**
7266
     * Read Unicode string with no string length field, but with known character count
7267
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7268
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
7269
     *
7270
     * @param string $subData
7271
     * @param int $characterCount
7272
     *
7273
     * @return array
7274
     */
7275 4
    private static function readUnicodeString($subData, $characterCount)
7276
    {
7277 4
        $value = '';
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

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

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

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

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

Loading history...
7278
7279
        // offset: 0: size: 1; option flags
7280
        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
7281 4
        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7282
7283
        // bit: 2; mask: 0x04; Asian phonetic settings
7284 4
        $hasAsian = (0x04) & ord($subData[0]) >> 2;
0 ignored issues
show
Unused Code introduced by
$hasAsian is not used, you could remove the assignment.

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

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

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

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

Loading history...
7285
7286
        // bit: 3; mask: 0x08; Rich-Text settings
7287 4
        $hasRichText = (0x08) & ord($subData[0]) >> 3;
0 ignored issues
show
Unused Code introduced by
$hasRichText is not used, you could remove the assignment.

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

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

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

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

Loading history...
7288
7289
        // 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...
7290
        // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
7291
        // needs to be fixed
7292 4
        $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
7293
7294
        return [
7295 4
            'value' => $value,
7296 4
            'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
7297
        ];
7298
    }
7299
7300
    /**
7301
     * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
7302
     * Example:  hello"world  -->  "hello""world".
7303
     *
7304
     * @param string $value UTF-8 encoded string
7305
     *
7306
     * @return string
7307
     */
7308 1
    private static function UTF8toExcelDoubleQuoted($value)
7309
    {
7310 1
        return '"' . str_replace('"', '""', $value) . '"';
7311
    }
7312
7313
    /**
7314
     * Reads first 8 bytes of a string and return IEEE 754 float.
7315
     *
7316
     * @param string $data Binary string that is at least 8 bytes long
7317
     *
7318
     * @return float
7319
     */
7320 4
    private static function extractNumber($data)
7321
    {
7322 4
        $rknumhigh = self::getInt4d($data, 4);
7323 4
        $rknumlow = self::getInt4d($data, 0);
7324 4
        $sign = ($rknumhigh & 0x80000000) >> 31;
7325 4
        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
7326 4
        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
7327 4
        $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
7328 4
        $mantissalow2 = ($rknumlow & 0x7fffffff);
7329 4
        $value = $mantissa / pow(2, (20 - $exp));
7330
7331 4
        if ($mantissalow1 != 0) {
7332 2
            $value += 1 / pow(2, (21 - $exp));
7333
        }
7334
7335 4
        $value += $mantissalow2 / pow(2, (52 - $exp));
7336 4
        if ($sign) {
7337
            $value *= -1;
7338
        }
7339
7340 4
        return $value;
7341
    }
7342
7343
    /**
7344
     * @param int $rknum
7345
     */
7346 1
    private static function getIEEE754($rknum)
7347
    {
7348 1
        if (($rknum & 0x02) != 0) {
7349
            $value = $rknum >> 2;
7350
        } else {
7351
            // changes by mmp, info on IEEE754 encoding from
7352
            // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
7353
            // The RK format calls for using only the most significant 30 bits
7354
            // of the 64 bit floating point value. The other 34 bits are assumed
7355
            // to be 0 so we use the upper 30 bits of $rknum as follows...
7356 1
            $sign = ($rknum & 0x80000000) >> 31;
7357 1
            $exp = ($rknum & 0x7ff00000) >> 20;
7358 1
            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
7359 1
            $value = $mantissa / pow(2, (20 - ($exp - 1023)));
7360 1
            if ($sign) {
7361
                $value = -1 * $value;
7362
            }
7363
            //end of changes by mmp
7364
        }
7365 1
        if (($rknum & 0x01) != 0) {
7366
            $value /= 100;
7367
        }
7368
7369 1
        return $value;
7370
    }
7371
7372
    /**
7373
     * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
7374
     *
7375
     * @param string $string
7376
     * @param bool $compressed
7377
     *
7378
     * @return string
7379
     */
7380 4
    private static function encodeUTF16($string, $compressed = false)
7381
    {
7382 4
        if ($compressed) {
7383 3
            $string = self::uncompressByteString($string);
7384
        }
7385
7386 4
        return \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
7387
    }
7388
7389
    /**
7390
     * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
7391
     *
7392
     * @param string $string
7393
     *
7394
     * @return string
7395
     */
7396 3
    private static function uncompressByteString($string)
7397
    {
7398 3
        $uncompressedString = '';
7399 3
        $strLen = strlen($string);
7400 3
        for ($i = 0; $i < $strLen; ++$i) {
7401 3
            $uncompressedString .= $string[$i] . "\0";
7402
        }
7403
7404 3
        return $uncompressedString;
7405
    }
7406
7407
    /**
7408
     * Convert string to UTF-8. Only used for BIFF5.
7409
     *
7410
     * @param string $string
7411
     *
7412
     * @return string
7413
     */
7414
    private function decodeCodepage($string)
7415
    {
7416
        return \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
7417
    }
7418
7419
    /**
7420
     * Read 16-bit unsigned integer.
7421
     *
7422
     * @param string $data
7423
     * @param int $pos
7424
     *
7425
     * @return int
7426
     */
7427 4
    public static function getInt2d($data, $pos)
7428
    {
7429 4
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
7430
    }
7431
7432
    /**
7433
     * Read 32-bit signed integer.
7434
     *
7435
     * @param string $data
7436
     * @param int $pos
7437
     *
7438
     * @return int
7439
     */
7440 4
    public static function getInt4d($data, $pos)
7441
    {
7442
        // FIX: represent numbers correctly on 64-bit system
7443
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
7444
        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
7445 4
        $_or_24 = ord($data[$pos + 3]);
7446 4
        if ($_or_24 >= 128) {
7447
            // negative number
7448 2
            $_ord_24 = -abs((256 - $_or_24) << 24);
7449
        } else {
7450 4
            $_ord_24 = ($_or_24 & 127) << 24;
7451
        }
7452
7453 4
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
7454
    }
7455
7456 1
    private function parseRichText($is = '')
7457
    {
7458 1
        $value = new \PhpOffice\PhpSpreadsheet\RichText();
7459 1
        $value->createText($is);
7460
7461 1
        return $value;
7462
    }
7463
}
7464