Completed
Push — develop ( 39b55d...e2e982 )
by Adrien
19:14
created

Xls::readFormula()   F

Complexity

Conditions 24
Paths 580

Size

Total Lines 120
Code Lines 59

Duplication

Lines 14
Ratio 11.67 %

Code Coverage

Tests 39
CRAP Score 40.1186

Importance

Changes 0
Metric Value
cc 24
eloc 59
nc 580
nop 0
dl 14
loc 120
ccs 39
cts 56
cp 0.6964
crap 40.1186
rs 2.3604
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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

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

Loading history...
170
171
    /**
172
     * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
173
     *
174
     * @var string
175
     */
176
    private $data;
177
178
    /**
179
     * Size in bytes of $this->data
180
     *
181
     * @var int
182
     */
183
    private $dataSize;
184
185
    /**
186
     * Current position in stream
187
     *
188
     * @var int
189
     */
190
    private $pos;
191
192
    /**
193
     * Workbook to be returned by the reader.
194
     *
195
     * @var \PhpOffice\PhpSpreadsheet\Spreadsheet
196
     */
197
    private $spreadsheet;
198
199
    /**
200
     * Worksheet that is currently being built by the reader.
201
     *
202
     * @var Worksheet
203
     */
204
    private $phpSheet;
205
206
    /**
207
     * BIFF version
208
     *
209
     * @var int
210
     */
211
    private $version;
212
213
    /**
214
     * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
215
     * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
216
     *
217
     * @var string
218
     */
219
    private $codepage;
220
221
    /**
222
     * Shared formats
223
     *
224
     * @var array
225
     */
226
    private $formats;
227
228
    /**
229
     * Shared fonts
230
     *
231
     * @var array
232
     */
233
    private $objFonts;
234
235
    /**
236
     * Color palette
237
     *
238
     * @var array
239
     */
240
    private $palette;
241
242
    /**
243
     * Worksheets
244
     *
245
     * @var array
246
     */
247
    private $sheets;
248
249
    /**
250
     * External books
251
     *
252
     * @var array
253
     */
254
    private $externalBooks;
255
256
    /**
257
     * REF structures. Only applies to BIFF8.
258
     *
259
     * @var array
260
     */
261
    private $ref;
262
263
    /**
264
     * External names
265
     *
266
     * @var array
267
     */
268
    private $externalNames;
269
270
    /**
271
     * Defined names
272
     *
273
     * @var array
274
     */
275
    private $definedname;
276
277
    /**
278
     * Shared strings. Only applies to BIFF8.
279
     *
280
     * @var array
281
     */
282
    private $sst;
283
284
    /**
285
     * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
286
     *
287
     * @var bool
288
     */
289
    private $frozen;
290
291
    /**
292
     * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
293
     *
294
     * @var bool
295
     */
296
    private $isFitToPages;
297
298
    /**
299
     * Objects. One OBJ record contributes with one entry.
300
     *
301
     * @var array
302
     */
303
    private $objs;
304
305
    /**
306
     * Text Objects. One TXO record corresponds with one entry.
307
     *
308
     * @var array
309
     */
310
    private $textObjects;
311
312
    /**
313
     * Cell Annotations (BIFF8)
314
     *
315
     * @var array
316
     */
317
    private $cellNotes;
318
319
    /**
320
     * The combined MSODRAWINGGROUP data
321
     *
322
     * @var string
323
     */
324
    private $drawingGroupData;
325
326
    /**
327
     * The combined MSODRAWING data (per sheet)
328
     *
329
     * @var string
330
     */
331
    private $drawingData;
332
333
    /**
334
     * Keep track of XF index
335
     *
336
     * @var int
337
     */
338
    private $xfIndex;
339
340
    /**
341
     * Mapping of XF index (that is a cell XF) to final index in cellXf collection
342
     *
343
     * @var array
344
     */
345
    private $mapCellXfIndex;
346
347
    /**
348
     * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
349
     *
350
     * @var array
351
     */
352
    private $mapCellStyleXfIndex;
353
354
    /**
355
     * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
356
     *
357
     * @var array
358
     */
359
    private $sharedFormulas;
360
361
    /**
362
     * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
363
     * refers to a shared formula.
364
     *
365
     * @var array
366
     */
367
    private $sharedFormulaParts;
368
369
    /**
370
     * The type of encryption in use
371
     *
372
     * @var int
373
     */
374
    private $encryption = 0;
375
376
    /**
377
     * The position in the stream after which contents are encrypted
378
     *
379
     * @var int
380
     */
381
    private $encryptionStartPos = false;
382
383
    /**
384
     * The current RC4 decryption object
385
     *
386
     * @var Xls\RC4
387
     */
388
    private $rc4Key = null;
389
390
    /**
391
     * The position in the stream that the RC4 decryption object was left at
392
     *
393
     * @var int
394
     */
395
    private $rc4Pos = 0;
396
397
    /**
398
     * The current MD5 context state
399
     *
400
     * @var string
401
     */
402
    private $md5Ctxt = null;
403
404
    /**
405
     * Create a new Xls Reader instance
406
     */
407 4
    public function __construct()
408
    {
409 4
        $this->readFilter = new DefaultReadFilter();
410 4
    }
411
412
    /**
413
     * Can the current IReader read the file?
414
     *
415
     * @param     string         $pFilename
416
     * @throws Exception
417
     * @return     bool
418
     */
419 2
    public function canRead($pFilename)
420
    {
421
        // Check if file exists
422 2
        if (!file_exists($pFilename)) {
423
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
424
        }
425
426
        try {
427
            // Use ParseXL for the hard work.
428 2
            $ole = new \PhpOffice\PhpSpreadsheet\Shared\OLERead();
429
430
            // get excel data
431 2
            $ole->read($pFilename);
432
433 2
            return true;
434
        } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
435
            return false;
436
        }
437
    }
438
439
    /**
440
     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object
441
     *
442
     * @param     string         $pFilename
443
     * @throws     Exception
444
     */
445
    public function listWorksheetNames($pFilename)
446
    {
447
        // Check if file exists
448
        if (!file_exists($pFilename)) {
449
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
450
        }
451
452
        $worksheetNames = [];
453
454
        // Read the OLE file
455
        $this->loadOLE($pFilename);
456
457
        // total byte size of Excel data (workbook global substream + sheet substreams)
458
        $this->dataSize = strlen($this->data);
459
460
        $this->pos = 0;
461
        $this->sheets = [];
462
463
        // Parse Workbook Global Substream
464 View Code Duplication
        while ($this->pos < $this->dataSize) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
465
            $code = self::getInt2d($this->data, $this->pos);
466
467
            switch ($code) {
468
                case self::XLS_TYPE_BOF:
469
                    $this->readBof();
470
                    break;
471
                case self::XLS_TYPE_SHEET:
472
                    $this->readSheet();
473
                    break;
474
                case self::XLS_TYPE_EOF:
475
                    $this->readDefault();
476
                    break 2;
477
                default:
478
                    $this->readDefault();
479
                    break;
480
            }
481
        }
482
483
        foreach ($this->sheets as $sheet) {
484
            if ($sheet['sheetType'] != 0x00) {
485
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
486
                continue;
487
            }
488
489
            $worksheetNames[] = $sheet['name'];
490
        }
491
492
        return $worksheetNames;
493
    }
494
495
    /**
496
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
497
     *
498
     * @param   string     $pFilename
499
     * @throws   Exception
500
     */
501
    public function listWorksheetInfo($pFilename)
502
    {
503
        // Check if file exists
504
        if (!file_exists($pFilename)) {
505
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
506
        }
507
508
        $worksheetInfo = [];
509
510
        // Read the OLE file
511
        $this->loadOLE($pFilename);
512
513
        // total byte size of Excel data (workbook global substream + sheet substreams)
514
        $this->dataSize = strlen($this->data);
515
516
        // initialize
517
        $this->pos = 0;
518
        $this->sheets = [];
519
520
        // Parse Workbook Global Substream
521 View Code Duplication
        while ($this->pos < $this->dataSize) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
522
            $code = self::getInt2d($this->data, $this->pos);
523
524
            switch ($code) {
525
                case self::XLS_TYPE_BOF:
526
                    $this->readBof();
527
                    break;
528
                case self::XLS_TYPE_SHEET:
529
                    $this->readSheet();
530
                    break;
531
                case self::XLS_TYPE_EOF:
532
                    $this->readDefault();
533
                    break 2;
534
                default:
535
                    $this->readDefault();
536
                    break;
537
            }
538
        }
539
540
        // Parse the individual sheets
541
        foreach ($this->sheets as $sheet) {
542
            if ($sheet['sheetType'] != 0x00) {
543
                // 0x00: Worksheet
544
                // 0x02: Chart
545
                // 0x06: Visual Basic module
546
                continue;
547
            }
548
549
            $tmpInfo = [];
550
            $tmpInfo['worksheetName'] = $sheet['name'];
551
            $tmpInfo['lastColumnLetter'] = 'A';
552
            $tmpInfo['lastColumnIndex'] = 0;
553
            $tmpInfo['totalRows'] = 0;
554
            $tmpInfo['totalColumns'] = 0;
555
556
            $this->pos = $sheet['offset'];
557
558
            while ($this->pos <= $this->dataSize - 4) {
559
                $code = self::getInt2d($this->data, $this->pos);
560
561
                switch ($code) {
562
                    case self::XLS_TYPE_RK:
563
                    case self::XLS_TYPE_LABELSST:
564
                    case self::XLS_TYPE_NUMBER:
565
                    case self::XLS_TYPE_FORMULA:
566
                    case self::XLS_TYPE_BOOLERR:
567
                    case self::XLS_TYPE_LABEL:
568
                        $length = self::getInt2d($this->data, $this->pos + 2);
569
                        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
570
571
                        // move stream pointer to next record
572
                        $this->pos += 4 + $length;
573
574
                        $rowIndex = self::getInt2d($recordData, 0) + 1;
575
                        $columnIndex = self::getInt2d($recordData, 2);
576
577
                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
578
                        $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
579
                        break;
580
                    case self::XLS_TYPE_BOF:
581
                        $this->readBof();
582
                        break;
583
                    case self::XLS_TYPE_EOF:
584
                        $this->readDefault();
585
                        break 2;
586
                    default:
587
                        $this->readDefault();
588
                        break;
589
                }
590
            }
591
592
            $tmpInfo['lastColumnLetter'] = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
593
            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
594
595
            $worksheetInfo[] = $tmpInfo;
596
        }
597
598
        return $worksheetInfo;
599
    }
600
601
    /**
602
     * Loads PhpSpreadsheet from file
603
     *
604
     * @param     string         $pFilename
605
     * @throws    Exception
606
     * @return    \PhpOffice\PhpSpreadsheet\Spreadsheet
607
     */
608 4
    public function load($pFilename)
609
    {
610
        // Read the OLE file
611 4
        $this->loadOLE($pFilename);
612
613
        // Initialisations
614 4
        $this->spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
615 4
        $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
616 4
        if (!$this->readDataOnly) {
617 4
            $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
618 4
            $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
619
        }
620
621
        // Read the summary information stream (containing meta data)
622 4
        $this->readSummaryInformation();
623
624
        // Read the Additional document summary information stream (containing application-specific meta data)
625 4
        $this->readDocumentSummaryInformation();
626
627
        // total byte size of Excel data (workbook global substream + sheet substreams)
628 4
        $this->dataSize = strlen($this->data);
629
630
        // initialize
631 4
        $this->pos = 0;
632 4
        $this->codepage = 'CP1252';
633 4
        $this->formats = [];
634 4
        $this->objFonts = [];
635 4
        $this->palette = [];
636 4
        $this->sheets = [];
637 4
        $this->externalBooks = [];
638 4
        $this->ref = [];
639 4
        $this->definedname = [];
640 4
        $this->sst = [];
641 4
        $this->drawingGroupData = '';
642 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...
643 4
        $this->mapCellXfIndex = [];
644 4
        $this->mapCellStyleXfIndex = [];
645
646
        // Parse Workbook Global Substream
647 4
        while ($this->pos < $this->dataSize) {
648 4
            $code = self::getInt2d($this->data, $this->pos);
649
650
            switch ($code) {
651 4
                case self::XLS_TYPE_BOF:
652 4
                    $this->readBof();
653 4
                    break;
654 4
                case self::XLS_TYPE_FILEPASS:
655
                    $this->readFilepass();
656
                    break;
657 4
                case self::XLS_TYPE_CODEPAGE:
658 4
                    $this->readCodepage();
659 4
                    break;
660 4
                case self::XLS_TYPE_DATEMODE:
661 4
                    $this->readDateMode();
662 4
                    break;
663 4
                case self::XLS_TYPE_FONT:
664 4
                    $this->readFont();
665 4
                    break;
666 4
                case self::XLS_TYPE_FORMAT:
667 4
                    $this->readFormat();
668 4
                    break;
669 4
                case self::XLS_TYPE_XF:
670 4
                    $this->readXf();
671 4
                    break;
672 4
                case self::XLS_TYPE_XFEXT:
673 3
                    $this->readXfExt();
674 3
                    break;
675 4
                case self::XLS_TYPE_STYLE:
676 4
                    $this->readStyle();
677 4
                    break;
678 4
                case self::XLS_TYPE_PALETTE:
679 2
                    $this->readPalette();
680 2
                    break;
681 4
                case self::XLS_TYPE_SHEET:
682 4
                    $this->readSheet();
683 4
                    break;
684 4
                case self::XLS_TYPE_EXTERNALBOOK:
685 3
                    $this->readExternalBook();
686 3
                    break;
687 4
                case self::XLS_TYPE_EXTERNNAME:
688
                    $this->readExternName();
689
                    break;
690 4
                case self::XLS_TYPE_EXTERNSHEET:
691 3
                    $this->readExternSheet();
692 3
                    break;
693 4
                case self::XLS_TYPE_DEFINEDNAME:
694 1
                    $this->readDefinedName();
695 1
                    break;
696 4
                case self::XLS_TYPE_MSODRAWINGGROUP:
697 3
                    $this->readMsoDrawingGroup();
698 3
                    break;
699 4
                case self::XLS_TYPE_SST:
700 4
                    $this->readSst();
701 4
                    break;
702 4
                case self::XLS_TYPE_EOF:
703 4
                    $this->readDefault();
704 4
                    break 2;
705
                default:
706 4
                    $this->readDefault();
707 4
                    break;
708
            }
709
        }
710
711
        // Resolve indexed colors for font, fill, and border colors
712
        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
713 4
        if (!$this->readDataOnly) {
714 4
            foreach ($this->objFonts as $objFont) {
715 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...
716 4
                    $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
717 4
                    $objFont->getColor()->setRGB($color['rgb']);
718
                }
719
            }
720
721 4
            foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
722
                // fill start and end color
723 4
                $fill = $objStyle->getFill();
724
725 4
                if (isset($fill->startcolorIndex)) {
726 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...
727 4
                    $fill->getStartColor()->setRGB($startColor['rgb']);
728
                }
729 4
                if (isset($fill->endcolorIndex)) {
730 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...
731 4
                    $fill->getEndColor()->setRGB($endColor['rgb']);
732
                }
733
734
                // border colors
735 4
                $top = $objStyle->getBorders()->getTop();
736 4
                $right = $objStyle->getBorders()->getRight();
737 4
                $bottom = $objStyle->getBorders()->getBottom();
738 4
                $left = $objStyle->getBorders()->getLeft();
739 4
                $diagonal = $objStyle->getBorders()->getDiagonal();
740
741 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...
742 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...
743 4
                    $top->getColor()->setRGB($borderTopColor['rgb']);
744
                }
745 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...
746 4
                    $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
747 4
                    $right->getColor()->setRGB($borderRightColor['rgb']);
748
                }
749 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...
750 4
                    $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
751 4
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
752
                }
753 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...
754 4
                    $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
755 4
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
756
                }
757 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...
758 4
                    $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
759 4
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
760
                }
761
            }
762
        }
763
764
        // treat MSODRAWINGGROUP records, workbook-level Escher
765 4
        if (!$this->readDataOnly && $this->drawingGroupData) {
766 2
            $escherWorkbook = new \PhpOffice\PhpSpreadsheet\Shared\Escher();
767 2
            $reader = new Xls\Escher($escherWorkbook);
768 2
            $escherWorkbook = $reader->load($this->drawingGroupData);
769
        }
770
771
        // Parse the individual sheets
772 4
        foreach ($this->sheets as $sheet) {
773 4
            if ($sheet['sheetType'] != 0x00) {
774
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
775
                continue;
776
            }
777
778
            // check if sheet should be skipped
779 4
            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
780
                continue;
781
            }
782
783
            // add sheet to PhpSpreadsheet object
784 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...
785
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
786
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
787
            //        name in line with the formula, not the reverse
788 4
            $this->phpSheet->setTitle($sheet['name'], false);
789 4
            $this->phpSheet->setSheetState($sheet['sheetState']);
790
791 4
            $this->pos = $sheet['offset'];
792
793
            // Initialize isFitToPages. May change after reading SHEETPR record.
794 4
            $this->isFitToPages = false;
795
796
            // Initialize drawingData
797 4
            $this->drawingData = '';
798
799
            // Initialize objs
800 4
            $this->objs = [];
801
802
            // Initialize shared formula parts
803 4
            $this->sharedFormulaParts = [];
804
805
            // Initialize shared formulas
806 4
            $this->sharedFormulas = [];
807
808
            // Initialize text objs
809 4
            $this->textObjects = [];
810
811
            // Initialize cell annotations
812 4
            $this->cellNotes = [];
813 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...
814
815 4
            while ($this->pos <= $this->dataSize - 4) {
816 4
                $code = self::getInt2d($this->data, $this->pos);
817
818
                switch ($code) {
819 4
                    case self::XLS_TYPE_BOF:
820 4
                        $this->readBof();
821 4
                        break;
822 4
                    case self::XLS_TYPE_PRINTGRIDLINES:
823 4
                        $this->readPrintGridlines();
824 4
                        break;
825 4
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
826 3
                        $this->readDefaultRowHeight();
827 3
                        break;
828 4
                    case self::XLS_TYPE_SHEETPR:
829 4
                        $this->readSheetPr();
830 4
                        break;
831 4
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
832
                        $this->readHorizontalPageBreaks();
833
                        break;
834 4
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
835
                        $this->readVerticalPageBreaks();
836
                        break;
837 4
                    case self::XLS_TYPE_HEADER:
838 4
                        $this->readHeader();
839 4
                        break;
840 4
                    case self::XLS_TYPE_FOOTER:
841 4
                        $this->readFooter();
842 4
                        break;
843 4
                    case self::XLS_TYPE_HCENTER:
844 4
                        $this->readHcenter();
845 4
                        break;
846 4
                    case self::XLS_TYPE_VCENTER:
847 4
                        $this->readVcenter();
848 4
                        break;
849 4
                    case self::XLS_TYPE_LEFTMARGIN:
850 4
                        $this->readLeftMargin();
851 4
                        break;
852 4
                    case self::XLS_TYPE_RIGHTMARGIN:
853 4
                        $this->readRightMargin();
854 4
                        break;
855 4
                    case self::XLS_TYPE_TOPMARGIN:
856 4
                        $this->readTopMargin();
857 4
                        break;
858 4
                    case self::XLS_TYPE_BOTTOMMARGIN:
859 4
                        $this->readBottomMargin();
860 4
                        break;
861 4
                    case self::XLS_TYPE_PAGESETUP:
862 4
                        $this->readPageSetup();
863 4
                        break;
864 4
                    case self::XLS_TYPE_PROTECT:
865 1
                        $this->readProtect();
866 1
                        break;
867 4
                    case self::XLS_TYPE_SCENPROTECT:
868
                        $this->readScenProtect();
869
                        break;
870 4
                    case self::XLS_TYPE_OBJECTPROTECT:
871
                        $this->readObjectProtect();
872
                        break;
873 4
                    case self::XLS_TYPE_PASSWORD:
874
                        $this->readPassword();
875
                        break;
876 4
                    case self::XLS_TYPE_DEFCOLWIDTH:
877 4
                        $this->readDefColWidth();
878 4
                        break;
879 4
                    case self::XLS_TYPE_COLINFO:
880 3
                        $this->readColInfo();
881 3
                        break;
882 4
                    case self::XLS_TYPE_DIMENSION:
883 4
                        $this->readDefault();
884 4
                        break;
885 4
                    case self::XLS_TYPE_ROW:
886 3
                        $this->readRow();
887 3
                        break;
888 4
                    case self::XLS_TYPE_DBCELL:
889 3
                        $this->readDefault();
890 3
                        break;
891 4
                    case self::XLS_TYPE_RK:
892 1
                        $this->readRk();
893 1
                        break;
894 4
                    case self::XLS_TYPE_LABELSST:
895 4
                        $this->readLabelSst();
896 4
                        break;
897 4
                    case self::XLS_TYPE_MULRK:
898
                        $this->readMulRk();
899
                        break;
900 4
                    case self::XLS_TYPE_NUMBER:
901 1
                        $this->readNumber();
902 1
                        break;
903 4
                    case self::XLS_TYPE_FORMULA:
904 2
                        $this->readFormula();
905 2
                        break;
906 4
                    case self::XLS_TYPE_SHAREDFMLA:
907
                        $this->readSharedFmla();
908
                        break;
909 4
                    case self::XLS_TYPE_BOOLERR:
910
                        $this->readBoolErr();
911
                        break;
912 4
                    case self::XLS_TYPE_MULBLANK:
913 1
                        $this->readMulBlank();
914 1
                        break;
915 4
                    case self::XLS_TYPE_LABEL:
916
                        $this->readLabel();
917
                        break;
918 4
                    case self::XLS_TYPE_BLANK:
919 2
                        $this->readBlank();
920 2
                        break;
921 4
                    case self::XLS_TYPE_MSODRAWING:
922 2
                        $this->readMsoDrawing();
923 2
                        break;
924 4
                    case self::XLS_TYPE_OBJ:
925 2
                        $this->readObj();
926 2
                        break;
927 4
                    case self::XLS_TYPE_WINDOW2:
928 4
                        $this->readWindow2();
929 4
                        break;
930 4
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
931 4
                        $this->readPageLayoutView();
932 4
                        break;
933 4
                    case self::XLS_TYPE_SCL:
934
                        $this->readScl();
935
                        break;
936 4
                    case self::XLS_TYPE_PANE:
937
                        $this->readPane();
938
                        break;
939 4
                    case self::XLS_TYPE_SELECTION:
940 4
                        $this->readSelection();
941 4
                        break;
942 4
                    case self::XLS_TYPE_MERGEDCELLS:
943 2
                        $this->readMergedCells();
944 2
                        break;
945 4
                    case self::XLS_TYPE_HYPERLINK:
946 2
                        $this->readHyperLink();
947 2
                        break;
948 4
                    case self::XLS_TYPE_DATAVALIDATIONS:
949
                        $this->readDataValidations();
950
                        break;
951 4
                    case self::XLS_TYPE_DATAVALIDATION:
952
                        $this->readDataValidation();
953
                        break;
954 4
                    case self::XLS_TYPE_SHEETLAYOUT:
955 2
                        $this->readSheetLayout();
956 2
                        break;
957 4
                    case self::XLS_TYPE_SHEETPROTECTION:
958 4
                        $this->readSheetProtection();
959 4
                        break;
960 4
                    case self::XLS_TYPE_RANGEPROTECTION:
961 1
                        $this->readRangeProtection();
962 1
                        break;
963 4
                    case self::XLS_TYPE_NOTE:
964 1
                        $this->readNote();
965 1
                        break;
966
                    //case self::XLS_TYPE_IMDATA:                $this->readImData();                    break;
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
967 4
                    case self::XLS_TYPE_TXO:
968 1
                        $this->readTextObject();
969 1
                        break;
970 4
                    case self::XLS_TYPE_CONTINUE:
971
                        $this->readContinue();
972
                        break;
973 4
                    case self::XLS_TYPE_EOF:
974 4
                        $this->readDefault();
975 4
                        break 2;
976
                    default:
977 4
                        $this->readDefault();
978 4
                        break;
979
                }
980
            }
981
982
            // treat MSODRAWING records, sheet-level Escher
983 4
            if (!$this->readDataOnly && $this->drawingData) {
984 2
                $escherWorksheet = new \PhpOffice\PhpSpreadsheet\Shared\Escher();
985 2
                $reader = new Xls\Escher($escherWorksheet);
986 2
                $escherWorksheet = $reader->load($this->drawingData);
987
988
                // get all spContainers in one long array, so they can be mapped to OBJ records
989 2
                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
990
            }
991
992
            // treat OBJ records
993 4
            foreach ($this->objs as $n => $obj) {
994
                // the first shape container never has a corresponding OBJ record, hence $n + 1
995 2
                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
996 2
                    $spContainer = $allSpContainers[$n + 1];
997
998
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
999 2
                    if ($spContainer->getNestingLevel() > 1) {
1000
                        continue;
1001
                    }
1002
1003
                    // calculate the width and height of the shape
1004 2
                    list($startColumn, $startRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($spContainer->getStartCoordinates());
1005 2
                    list($endColumn, $endRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($spContainer->getEndCoordinates());
1006
1007 2
                    $startOffsetX = $spContainer->getStartOffsetX();
1008 2
                    $startOffsetY = $spContainer->getStartOffsetY();
1009 2
                    $endOffsetX = $spContainer->getEndOffsetX();
1010 2
                    $endOffsetY = $spContainer->getEndOffsetY();
1011
1012 2
                    $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
1013 2
                    $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
1014
1015
                    // calculate offsetX and offsetY of the shape
1016 2
                    $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024;
1017 2
                    $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256;
1018
1019 2
                    switch ($obj['otObjType']) {
1020 2
                        case 0x19:
1021
                            // Note
1022 1
                            if (isset($this->cellNotes[$obj['idObjID']])) {
1023 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...
1024
1025 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...
1026 1
                                    $textObject = $this->textObjects[$obj['idObjID']];
1027 1
                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
1028
                                }
1029
                            }
1030 1
                            break;
1031 2
                        case 0x08:
1032
                            // picture
1033
                            // get index to BSE entry (1-based)
1034 2
                            $BSEindex = $spContainer->getOPT(0x0104);
1035 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...
1036 2
                            $BSE = $BSECollection[$BSEindex - 1];
1037 2
                            $blipType = $BSE->getBlipType();
1038
1039
                            // need check because some blip types are not supported by Escher reader such as EMF
1040 2
                            if ($blip = $BSE->getBlip()) {
1041 2
                                $ih = imagecreatefromstring($blip->getData());
1042 2
                                $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing();
1043 2
                                $drawing->setImageResource($ih);
1044
1045
                                // width, height, offsetX, offsetY
1046 2
                                $drawing->setResizeProportional(false);
1047 2
                                $drawing->setWidth($width);
1048 2
                                $drawing->setHeight($height);
1049 2
                                $drawing->setOffsetX($offsetX);
1050 2
                                $drawing->setOffsetY($offsetY);
1051
1052
                                switch ($blipType) {
1053 2
                                    case \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_JPEG:
1054 2
                                        $drawing->setRenderingFunction(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_JPEG);
1055 2
                                        $drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_JPEG);
1056 2
                                        break;
1057 2
                                    case \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_PNG:
1058 2
                                        $drawing->setRenderingFunction(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_PNG);
1059 2
                                        $drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_PNG);
1060 2
                                        break;
1061
                                }
1062
1063 2
                                $drawing->setWorksheet($this->phpSheet);
1064 2
                                $drawing->setCoordinates($spContainer->getStartCoordinates());
1065
                            }
1066 2
                            break;
1067
                        default:
1068
                            // other object type
1069 2
                            break;
1070
                    }
1071
                }
1072
            }
1073
1074
            // treat SHAREDFMLA records
1075 4
            if ($this->version == self::XLS_BIFF8) {
1076 4
                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
1077
                    list($column, $row) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($cell);
1078
                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
1079
                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
1080
                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
1081
                    }
1082
                }
1083
            }
1084
1085 4
            if (!empty($this->cellNotes)) {
1086 1
                foreach ($this->cellNotes as $note => $noteDetails) {
1087 1
                    if (!isset($noteDetails['objTextData'])) {
1088 View Code Duplication
                        if (isset($this->textObjects[$note])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1089
                            $textObject = $this->textObjects[$note];
1090
                            $noteDetails['objTextData'] = $textObject;
1091
                        } else {
1092
                            $noteDetails['objTextData']['text'] = '';
1093
                        }
1094
                    }
1095 1
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
1096 4
                    $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
1097
                }
1098
            }
1099
        }
1100
1101
        // add the named ranges (defined names)
1102 4
        foreach ($this->definedname as $definedName) {
1103 1
            if ($definedName['isBuiltInName']) {
1104
                switch ($definedName['name']) {
1105
                    case pack('C', 0x06):
1106
                        // print area
1107
                        //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1108
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1109
1110
                        $extractedRanges = [];
1111
                        foreach ($ranges as $range) {
1112
                            // $range should look like one of these
1113
                            //        Foo!$C$7:$J$66
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1114
                            //        Bar!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1115
                            $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
1116
                            $sheetName = trim($explodes[0], "'");
1117
                            if (count($explodes) == 2) {
1118
                                if (strpos($explodes[1], ':') === false) {
1119
                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
1120
                                }
1121
                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
1122
                            }
1123
                        }
1124
                        if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
0 ignored issues
show
Bug introduced by
The variable $sheetName does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1125
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
1126
                        }
1127
                        break;
1128
                    case pack('C', 0x07):
1129
                        // print titles (repeating rows)
1130
                        // Assuming BIFF8, there are 3 cases
1131
                        // 1. repeating rows
1132
                        //        formula looks like this: Sheet!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1133
                        //        rows 1-2 repeat
1134
                        // 2. repeating columns
1135
                        //        formula looks like this: Sheet!$A$1:$B$65536
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1136
                        //        columns A-B repeat
1137
                        // 3. both repeating rows and repeating columns
1138
                        //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1139
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1140
                        foreach ($ranges as $range) {
1141
                            // $range should look like this one of these
1142
                            //        Sheet!$A$1:$B$65536
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1143
                            //        Sheet!$A$1:$IV$2
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1144
                            $explodes = explode('!', $range);
1145
                            if (count($explodes) == 2) {
1146
                                if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
1147
                                    $extractedRange = $explodes[1];
1148
                                    $extractedRange = str_replace('$', '', $extractedRange);
1149
1150
                                    $coordinateStrings = explode(':', $extractedRange);
1151
                                    if (count($coordinateStrings) == 2) {
1152
                                        list($firstColumn, $firstRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[0]);
1153
                                        list($lastColumn, $lastRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[1]);
1154
1155
                                        if ($firstColumn == 'A' and $lastColumn == 'IV') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
1159
                                            // then we have repeating columns
1160
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
1161
                                        }
1162
                                    }
1163
                                }
1164
                            }
1165
                        }
1166
                        break;
1167
                }
1168
            } else {
1169
                // Extract range
1170 1
                $explodes = explode('!', $definedName['formula']);
1171
1172 1
                if (count($explodes) == 2) {
1173 1
                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1174 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...
1175 1
                        $extractedRange = $explodes[1];
1176 1
                        $extractedRange = str_replace('$', '', $extractedRange);
1177
1178 1
                        $localOnly = ($definedName['scope'] == 0) ? false : true;
1179
1180 1
                        $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
1181
1182 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...
1183
                    }
1184 1
                } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

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

These else branches can be removed.

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

could be turned into

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

This is much more concise to read.

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

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

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

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

Loading history...
1281
        // offset: 4; size: 2; OS version
1282
        // offset: 6; size: 2; OS indicator
1283
        // offset: 8; size: 16
1284
        // offset: 24; size: 4; section count
1285 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...
1286
1287
        // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
1288
        // offset: 44; size: 4
1289 4
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1290
1291
        // section header
1292
        // offset: $secOffset; size: 4; section length
1293 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...
1294
1295
        // offset: $secOffset+4; size: 4; property count
1296 4
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1297
1298
        // initialize code page (used to resolve string values)
1299 4
        $codePage = 'CP1252';
1300
1301
        // offset: ($secOffset+8); size: var
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1302
        // loop through property decarations and properties
1303 4
        for ($i = 0; $i < $countProperties; ++$i) {
1304
            // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1305 4
            $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
1306
1307
            // Use value of property id as appropriate
1308
            // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1309 4
            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
1310
1311 4
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1312
1313
            // initialize property value
1314 4
            $value = null;
1315
1316
            // extract property value based on property type
1317
            switch ($type) {
1318 4
                case 0x02: // 2 byte signed integer
1319 4
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1320 4
                    break;
1321 4
                case 0x03: // 4 byte signed integer
1322 4
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1323 4
                    break;
1324 4
                case 0x13: // 4 byte unsigned integer
1325
                    // not needed yet, fix later if necessary
1326
                    break;
1327 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...
1328 4
                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1329 4
                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1330 4
                    $value = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1331 4
                    $value = rtrim($value);
1332 4
                    break;
1333 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...
1334
                    // PHP-time
1335 4
                    $value = \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1336 4
                    break;
1337
                case 0x47: // Clipboard format
1338
                    // not needed yet, fix later if necessary
1339
                    break;
1340
            }
1341
1342
            switch ($id) {
1343 4
                case 0x01:    //    Code Page
1344 4
                    $codePage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($value);
1345 4
                    break;
1346 4
                case 0x02:    //    Title
1347 3
                    $this->spreadsheet->getProperties()->setTitle($value);
1348 3
                    break;
1349 4
                case 0x03:    //    Subject
1350 3
                    $this->spreadsheet->getProperties()->setSubject($value);
1351 3
                    break;
1352 4
                case 0x04:    //    Author (Creator)
1353 4
                    $this->spreadsheet->getProperties()->setCreator($value);
1354 4
                    break;
1355 4
                case 0x05:    //    Keywords
1356 3
                    $this->spreadsheet->getProperties()->setKeywords($value);
1357 3
                    break;
1358 4
                case 0x06:    //    Comments (Description)
1359 3
                    $this->spreadsheet->getProperties()->setDescription($value);
1360 3
                    break;
1361 4
                case 0x07:    //    Template
1362
                    //    Not supported by PhpSpreadsheet
1363
                    break;
1364 4
                case 0x08:    //    Last Saved By (LastModifiedBy)
1365 4
                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
1366 4
                    break;
1367 4
                case 0x09:    //    Revision
1368
                    //    Not supported by PhpSpreadsheet
1369
                    break;
1370 4
                case 0x0A:    //    Total Editing Time
1371
                    //    Not supported by PhpSpreadsheet
1372
                    break;
1373 4
                case 0x0B:    //    Last Printed
1374
                    //    Not supported by PhpSpreadsheet
1375
                    break;
1376 4
                case 0x0C:    //    Created Date/Time
1377 4
                    $this->spreadsheet->getProperties()->setCreated($value);
1378 4
                    break;
1379 4
                case 0x0D:    //    Modified Date/Time
1380 4
                    $this->spreadsheet->getProperties()->setModified($value);
1381 4
                    break;
1382 4
                case 0x0E:    //    Number of Pages
1383
                    //    Not supported by PhpSpreadsheet
1384
                    break;
1385 4
                case 0x0F:    //    Number of Words
1386
                    //    Not supported by PhpSpreadsheet
1387
                    break;
1388 4
                case 0x10:    //    Number of Characters
1389
                    //    Not supported by PhpSpreadsheet
1390
                    break;
1391 4
                case 0x11:    //    Thumbnail
1392
                    //    Not supported by PhpSpreadsheet
1393
                    break;
1394 4
                case 0x12:    //    Name of creating application
1395
                    //    Not supported by PhpSpreadsheet
1396 2
                    break;
1397 4
                case 0x13:    //    Security
1398
                    //    Not supported by PhpSpreadsheet
1399 4
                    break;
1400
            }
1401
        }
1402 4
    }
1403
1404
    /**
1405
     * Read additional document summary information
1406
     */
1407 4
    private function readDocumentSummaryInformation()
1408
    {
1409 4
        if (!isset($this->documentSummaryInformation)) {
1410
            return;
1411
        }
1412
1413
        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
1414
        //    offset: 2;    size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1415
        //    offset: 4;    size: 2;    OS version
1416
        //    offset: 6;    size: 2;    OS indicator
1417
        //    offset: 8;    size: 16
1418
        //    offset: 24;    size: 4;    section count
1419 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...
1420
1421
        // offset: 28;    size: 16;    first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
1422
        // offset: 44;    size: 4;    first section offset
1423 4
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1424
1425
        //    section header
1426
        //    offset: $secOffset;    size: 4;    section length
1427 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...
1428
1429
        //    offset: $secOffset+4;    size: 4;    property count
1430 4
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1431
1432
        // initialize code page (used to resolve string values)
1433 4
        $codePage = 'CP1252';
1434
1435
        //    offset: ($secOffset+8);    size: var
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1436
        //    loop through property decarations and properties
1437 4
        for ($i = 0; $i < $countProperties; ++$i) {
1438
            //    offset: ($secOffset+8) + (8 * $i);    size: 4;    property ID
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1439 4
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
1440
1441
            // Use value of property id as appropriate
1442
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1443 4
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
1444
1445 4
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1446
1447
            // initialize property value
1448 4
            $value = null;
1449
1450
            // extract property value based on property type
1451
            switch ($type) {
1452 4
                case 0x02:    //    2 byte signed integer
1453 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1454 4
                    break;
1455 4
                case 0x03:    //    4 byte signed integer
1456 4
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1457 4
                    break;
1458 4
                case 0x0B:  // Boolean
1459 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1460 4
                    $value = ($value == 0 ? false : true);
1461 4
                    break;
1462 4
                case 0x13:    //    4 byte unsigned integer
1463
                    // not needed yet, fix later if necessary
1464
                    break;
1465 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...
1466 4
                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1467 4
                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1468 4
                    $value = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1469 4
                    $value = rtrim($value);
1470 4
                    break;
1471 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...
1472
                    // PHP-Time
1473
                    $value = \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1474
                    break;
1475 4
                case 0x47:    //    Clipboard format
1476
                    // not needed yet, fix later if necessary
1477
                    break;
1478
            }
1479
1480
            switch ($id) {
1481 4
                case 0x01:    //    Code Page
1482 4
                    $codePage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($value);
1483 4
                    break;
1484 4
                case 0x02:    //    Category
1485 3
                    $this->spreadsheet->getProperties()->setCategory($value);
1486 3
                    break;
1487 4
                case 0x03:    //    Presentation Target
1488
                    //    Not supported by PhpSpreadsheet
1489
                    break;
1490 4
                case 0x04:    //    Bytes
1491
                    //    Not supported by PhpSpreadsheet
1492
                    break;
1493 4
                case 0x05:    //    Lines
1494
                    //    Not supported by PhpSpreadsheet
1495
                    break;
1496 4
                case 0x06:    //    Paragraphs
1497
                    //    Not supported by PhpSpreadsheet
1498
                    break;
1499 4
                case 0x07:    //    Slides
1500
                    //    Not supported by PhpSpreadsheet
1501
                    break;
1502 4
                case 0x08:    //    Notes
1503
                    //    Not supported by PhpSpreadsheet
1504
                    break;
1505 4
                case 0x09:    //    Hidden Slides
1506
                    //    Not supported by PhpSpreadsheet
1507
                    break;
1508 4
                case 0x0A:    //    MM Clips
1509
                    //    Not supported by PhpSpreadsheet
1510
                    break;
1511 4
                case 0x0B:    //    Scale Crop
1512
                    //    Not supported by PhpSpreadsheet
1513 4
                    break;
1514 4
                case 0x0C:    //    Heading Pairs
1515
                    //    Not supported by PhpSpreadsheet
1516 4
                    break;
1517 4
                case 0x0D:    //    Titles of Parts
1518
                    //    Not supported by PhpSpreadsheet
1519 4
                    break;
1520 4
                case 0x0E:    //    Manager
1521 1
                    $this->spreadsheet->getProperties()->setManager($value);
1522 1
                    break;
1523 4
                case 0x0F:    //    Company
1524 3
                    $this->spreadsheet->getProperties()->setCompany($value);
1525 3
                    break;
1526 4
                case 0x10:    //    Links up-to-date
1527
                    //    Not supported by PhpSpreadsheet
1528 4
                    break;
1529
            }
1530
        }
1531 4
    }
1532
1533
    /**
1534
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1535
     */
1536 4
    private function readDefault()
1537
    {
1538 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1539
1540
        // move stream pointer to next record
1541 4
        $this->pos += 4 + $length;
1542 4
    }
1543
1544
    /**
1545
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1546
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1547
     */
1548 1
    private function readNote()
1549
    {
1550 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1551 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1552
1553
        // move stream pointer to next record
1554 1
        $this->pos += 4 + $length;
1555
1556 1
        if ($this->readDataOnly) {
1557
            return;
1558
        }
1559
1560 1
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1561 1
        if ($this->version == self::XLS_BIFF8) {
1562 1
            $noteObjID = self::getInt2d($recordData, 6);
1563 1
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1564 1
            $noteAuthor = $noteAuthor['value'];
1565 1
            $this->cellNotes[$noteObjID] = [
1566 1
                'cellRef' => $cellAddress,
1567 1
                'objectID' => $noteObjID,
1568 1
                'author' => $noteAuthor,
1569
            ];
1570
        } else {
1571
            $extension = false;
1572
            if ($cellAddress == '$B$65536') {
1573
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1574
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1575
                //        max 2048 bytes will probably throw a wobbly.
1576
                $row = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$row is not used, you could remove the assignment.

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

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

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

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

Loading history...
1577
                $extension = true;
1578
                $cellAddress = array_pop(array_keys($this->phpSheet->getComments()));
0 ignored issues
show
Bug introduced by
array_keys($this->phpSheet->getComments()) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
1579
            }
1580
1581
            $cellAddress = str_replace('$', '', $cellAddress);
1582
            $noteLength = self::getInt2d($recordData, 4);
0 ignored issues
show
Unused Code introduced by
$noteLength is not used, you could remove the assignment.

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

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

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

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

Loading history...
1583
            $noteText = trim(substr($recordData, 6));
1584
1585
            if ($extension) {
1586
                //    Concatenate this extension with the currently set comment for the cell
1587
                $comment = $this->phpSheet->getComment($cellAddress);
1588
                $commentText = $comment->getText()->getPlainText();
1589
                $comment->setText($this->parseRichText($commentText . $noteText));
1590
            } else {
1591
                //    Set comment for the cell
1592
                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
1593
//                                                    ->setAuthor($author)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1594
            }
1595
        }
1596 1
    }
1597
1598
    /**
1599
     *    The TEXT Object record contains the text associated with a cell annotation.
1600
     */
1601 1
    private function readTextObject()
1602
    {
1603 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1604 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1605
1606
        // move stream pointer to next record
1607 1
        $this->pos += 4 + $length;
1608
1609 1
        if ($this->readDataOnly) {
1610
            return;
1611
        }
1612
1613
        // recordData consists of an array of subrecords looking like this:
1614
        //    grbit: 2 bytes; Option Flags
1615
        //    rot: 2 bytes; rotation
1616
        //    cchText: 2 bytes; length of the text (in the first continue record)
1617
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1618
        // followed by the continuation records containing the actual text and formatting
1619 1
        $grbitOpts = self::getInt2d($recordData, 0);
1620 1
        $rot = self::getInt2d($recordData, 2);
1621 1
        $cchText = self::getInt2d($recordData, 10);
1622 1
        $cbRuns = self::getInt2d($recordData, 12);
1623 1
        $text = $this->getSplicedRecordData();
1624
1625 1
        $this->textObjects[$this->textObjRef] = [
1626 1
            'text' => substr($text['recordData'], $text['spliceOffsets'][0] + 1, $cchText),
1627 1
            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
1628 1
            'alignment' => $grbitOpts,
1629 1
            'rotation' => $rot,
1630
        ];
1631 1
    }
1632
1633
    /**
1634
     * Read BOF
1635
     */
1636 4
    private function readBof()
1637
    {
1638 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1639 4
        $recordData = substr($this->data, $this->pos + 4, $length);
1640
1641
        // move stream pointer to next record
1642 4
        $this->pos += 4 + $length;
1643
1644
        // offset: 2; size: 2; type of the following data
1645 4
        $substreamType = self::getInt2d($recordData, 2);
1646
1647
        switch ($substreamType) {
1648 4
            case self::XLS_WORKBOOKGLOBALS:
1649 4
                $version = self::getInt2d($recordData, 0);
1650 4
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1651
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1652
                }
1653 4
                $this->version = $version;
1654 4
                break;
1655 4
            case self::XLS_WORKSHEET:
1656
                // do not use this version information for anything
1657
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1658 4
                break;
1659
            default:
1660
                // substream, e.g. chart
1661
                // just skip the entire substream
1662
                do {
1663
                    $code = self::getInt2d($this->data, $this->pos);
1664
                    $this->readDefault();
1665
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1666
                break;
1667
        }
1668 4
    }
1669
1670
    /**
1671
     * FILEPASS
1672
     *
1673
     * This record is part of the File Protection Block. It
1674
     * contains information about the read/write password of the
1675
     * file. All record contents following this record will be
1676
     * encrypted.
1677
     *
1678
     * --    "OpenOffice.org's Documentation of the Microsoft
1679
     *         Excel File Format"
1680
     *
1681
     * The decryption functions and objects used from here on in
1682
     * are based on the source of Spreadsheet-ParseExcel:
1683
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1684
     */
1685
    private function readFilepass()
1686
    {
1687
        $length = self::getInt2d($this->data, $this->pos + 2);
1688
1689
        if ($length != 54) {
1690
            throw new Exception('Unexpected file pass record length');
1691
        }
1692
1693
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1694
1695
        // move stream pointer to next record
1696
        $this->pos += 4 + $length;
1697
1698
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1699
            throw new Exception('Decryption password incorrect');
1700
        }
1701
1702
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1703
1704
        // Decryption required from the record after next onwards
1705
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1706
    }
1707
1708
    /**
1709
     * Make an RC4 decryptor for the given block
1710
     *
1711
     * @param int         Block for which to create decrypto
1712
     * @param string $valContext MD5 context state
1713
     *
1714
     * @return Xls\RC4
1715
     */
1716
    private function makeKey($block, $valContext)
1717
    {
1718
        $pwarray = str_repeat("\0", 64);
1719
1720
        for ($i = 0; $i < 5; ++$i) {
1721
            $pwarray[$i] = $valContext[$i];
1722
        }
1723
1724
        $pwarray[5] = chr($block & 0xff);
1725
        $pwarray[6] = chr(($block >> 8) & 0xff);
1726
        $pwarray[7] = chr(($block >> 16) & 0xff);
1727
        $pwarray[8] = chr(($block >> 24) & 0xff);
1728
1729
        $pwarray[9] = "\x80";
1730
        $pwarray[56] = "\x48";
1731
1732
        $md5 = new Xls\MD5();
1733
        $md5->add($pwarray);
1734
1735
        $s = $md5->getContext();
1736
1737
        return new Xls\RC4($s);
1738
    }
1739
1740
    /**
1741
     * Verify RC4 file password
1742
     *
1743
     * @param string $password        Password to check
1744
     * @param string $docid           Document id
1745
     * @param string $salt_data       Salt data
1746
     * @param string $hashedsalt_data Hashed salt data
1747
     * @param string $valContext     Set to the MD5 context of the value
1748
     *
1749
     * @return bool Success
1750
     */
1751
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1752
    {
1753
        $pwarray = str_repeat("\0", 64);
1754
1755
        for ($i = 0; $i < strlen($password); ++$i) {
1756
            $o = ord(substr($password, $i, 1));
1757
            $pwarray[2 * $i] = chr($o & 0xff);
1758
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1759
        }
1760
        $pwarray[2 * $i] = chr(0x80);
1761
        $pwarray[56] = chr(($i << 4) & 0xff);
1762
1763
        $md5 = new Xls\MD5();
1764
        $md5->add($pwarray);
1765
1766
        $mdContext1 = $md5->getContext();
1767
1768
        $offset = 0;
1769
        $keyoffset = 0;
1770
        $tocopy = 5;
1771
1772
        $md5->reset();
1773
1774
        while ($offset != 16) {
1775
            if ((64 - $offset) < 5) {
1776
                $tocopy = 64 - $offset;
1777
            }
1778
            for ($i = 0; $i <= $tocopy; ++$i) {
1779
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1780
            }
1781
            $offset += $tocopy;
1782
1783
            if ($offset == 64) {
1784
                $md5->add($pwarray);
1785
                $keyoffset = $tocopy;
1786
                $tocopy = 5 - $tocopy;
1787
                $offset = 0;
1788
                continue;
1789
            }
1790
1791
            $keyoffset = 0;
1792
            $tocopy = 5;
1793
            for ($i = 0; $i < 16; ++$i) {
1794
                $pwarray[$offset + $i] = $docid[$i];
1795
            }
1796
            $offset += 16;
1797
        }
1798
1799
        $pwarray[16] = "\x80";
1800
        for ($i = 0; $i < 47; ++$i) {
1801
            $pwarray[17 + $i] = "\0";
1802
        }
1803
        $pwarray[56] = "\x80";
1804
        $pwarray[57] = "\x0a";
1805
1806
        $md5->add($pwarray);
1807
        $valContext = $md5->getContext();
1808
1809
        $key = $this->makeKey(0, $valContext);
1810
1811
        $salt = $key->RC4($salt_data);
1812
        $hashedsalt = $key->RC4($hashedsalt_data);
1813
1814
        $salt .= "\x80" . str_repeat("\0", 47);
1815
        $salt[56] = "\x80";
1816
1817
        $md5->reset();
1818
        $md5->add($salt);
1819
        $mdContext2 = $md5->getContext();
1820
1821
        return $mdContext2 == $hashedsalt;
1822
    }
1823
1824
    /**
1825
     * CODEPAGE
1826
     *
1827
     * This record stores the text encoding used to write byte
1828
     * strings, stored as MS Windows code page identifier.
1829
     *
1830
     * --    "OpenOffice.org's Documentation of the Microsoft
1831
     *         Excel File Format"
1832
     */
1833 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...
1834
    {
1835 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1836 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1837
1838
        // move stream pointer to next record
1839 4
        $this->pos += 4 + $length;
1840
1841
        // offset: 0; size: 2; code page identifier
1842 4
        $codepage = self::getInt2d($recordData, 0);
1843
1844 4
        $this->codepage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($codepage);
1845 4
    }
1846
1847
    /**
1848
     * DATEMODE
1849
     *
1850
     * This record specifies the base date for displaying date
1851
     * values. All dates are stored as count of days past this
1852
     * base date. In BIFF2-BIFF4 this record is part of the
1853
     * Calculation Settings Block. In BIFF5-BIFF8 it is
1854
     * stored in the Workbook Globals Substream.
1855
     *
1856
     * --    "OpenOffice.org's Documentation of the Microsoft
1857
     *         Excel File Format"
1858
     */
1859 4
    private function readDateMode()
1860
    {
1861 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1862 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1863
1864
        // move stream pointer to next record
1865 4
        $this->pos += 4 + $length;
1866
1867
        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
1868 4
        \PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900);
1869 4
        if (ord($recordData{0}) == 1) {
1870
            \PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR_MAC_1904);
1871
        }
1872 4
    }
1873
1874
    /**
1875
     * Read a FONT record
1876
     */
1877 4
    private function readFont()
1878
    {
1879 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1880 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1881
1882
        // move stream pointer to next record
1883 4
        $this->pos += 4 + $length;
1884
1885 4
        if (!$this->readDataOnly) {
1886 4
            $objFont = new \PhpOffice\PhpSpreadsheet\Style\Font();
1887
1888
            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
1889 4
            $size = self::getInt2d($recordData, 0);
1890 4
            $objFont->setSize($size / 20);
1891
1892
            // offset: 2; size: 2; option flags
1893
            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
1894
            // bit: 1; mask 0x0002; italic
1895 4
            $isItalic = (0x0002 & self::getInt2d($recordData, 2)) >> 1;
1896 4
            if ($isItalic) {
1897 4
                $objFont->setItalic(true);
1898
            }
1899
1900
            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
1901
            // bit: 3; mask 0x0008; strike
1902 4
            $isStrike = (0x0008 & self::getInt2d($recordData, 2)) >> 3;
1903 4
            if ($isStrike) {
1904
                $objFont->setStrikethrough(true);
1905
            }
1906
1907
            // offset: 4; size: 2; colour index
1908 4
            $colorIndex = self::getInt2d($recordData, 4);
1909 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...
1910
1911
            // offset: 6; size: 2; font weight
1912 4
            $weight = self::getInt2d($recordData, 6);
1913
            switch ($weight) {
1914 4
                case 0x02BC:
1915 4
                    $objFont->setBold(true);
1916 4
                    break;
1917
            }
1918
1919
            // offset: 8; size: 2; escapement type
1920 4
            $escapement = self::getInt2d($recordData, 8);
1921
            switch ($escapement) {
1922 4
                case 0x0001:
1923
                    $objFont->setSuperScript(true);
1924
                    break;
1925 4
                case 0x0002:
1926
                    $objFont->setSubScript(true);
1927
                    break;
1928
            }
1929
1930
            // offset: 10; size: 1; underline type
1931 4
            $underlineType = ord($recordData{10});
1932
            switch ($underlineType) {
1933 4
                case 0x00:
1934 4
                    break; // no underline
1935 2
                case 0x01:
1936 2
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1937 2
                    break;
1938
                case 0x02:
1939
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE);
1940
                    break;
1941
                case 0x21:
1942
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING);
1943
                    break;
1944
                case 0x22:
1945
                    $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING);
1946
                    break;
1947
            }
1948
1949
            // offset: 11; size: 1; font family
1950
            // offset: 12; size: 1; character set
1951
            // offset: 13; size: 1; not used
1952
            // offset: 14; size: var; font name
1953 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...
1954 4
                $string = self::readUnicodeStringShort(substr($recordData, 14));
1955
            } else {
1956
                $string = $this->readByteStringShort(substr($recordData, 14));
1957
            }
1958 4
            $objFont->setName($string['value']);
1959
1960 4
            $this->objFonts[] = $objFont;
1961
        }
1962 4
    }
1963
1964
    /**
1965
     * FORMAT
1966
     *
1967
     * This record contains information about a number format.
1968
     * All FORMAT records occur together in a sequential list.
1969
     *
1970
     * In BIFF2-BIFF4 other records referencing a FORMAT record
1971
     * contain a zero-based index into this list. From BIFF5 on
1972
     * the FORMAT record contains the index itself that will be
1973
     * used by other records.
1974
     *
1975
     * --    "OpenOffice.org's Documentation of the Microsoft
1976
     *         Excel File Format"
1977
     */
1978 4
    private function readFormat()
1979
    {
1980 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1981 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1982
1983
        // move stream pointer to next record
1984 4
        $this->pos += 4 + $length;
1985
1986 4
        if (!$this->readDataOnly) {
1987 4
            $indexCode = self::getInt2d($recordData, 0);
1988
1989 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...
1990 4
                $string = self::readUnicodeStringLong(substr($recordData, 2));
1991
            } else {
1992
                // BIFF7
1993
                $string = $this->readByteStringShort(substr($recordData, 2));
1994
            }
1995
1996 4
            $formatString = $string['value'];
1997 4
            $this->formats[$indexCode] = $formatString;
1998
        }
1999 4
    }
2000
2001
    /**
2002
     * XF - Extended Format
2003
     *
2004
     * This record contains formatting information for cells, rows, columns or styles.
2005
     * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
2006
     * and 1 cell XF.
2007
     * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
2008
     * and XF record 15 is a cell XF
2009
     * We only read the first cell style XF and skip the remaining cell style XF records
2010
     * We read all cell XF records.
2011
     *
2012
     * --    "OpenOffice.org's Documentation of the Microsoft
2013
     *         Excel File Format"
2014
     */
2015 4
    private function readXf()
2016
    {
2017 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2018 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2019
2020
        // move stream pointer to next record
2021 4
        $this->pos += 4 + $length;
2022
2023 4
        $objStyle = new \PhpOffice\PhpSpreadsheet\Style();
2024
2025 4
        if (!$this->readDataOnly) {
2026
            // offset:  0; size: 2; Index to FONT record
2027 4
            if (self::getInt2d($recordData, 0) < 4) {
2028 4
                $fontIndex = self::getInt2d($recordData, 0);
2029
            } else {
2030
                // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
2031
                // check the OpenOffice documentation of the FONT record
2032 4
                $fontIndex = self::getInt2d($recordData, 0) - 1;
2033
            }
2034 4
            $objStyle->setFont($this->objFonts[$fontIndex]);
2035
2036
            // offset:  2; size: 2; Index to FORMAT record
2037 4
            $numberFormatIndex = self::getInt2d($recordData, 2);
2038 4
            if (isset($this->formats[$numberFormatIndex])) {
2039
                // then we have user-defined format code
2040 3
                $numberformat = ['code' => $this->formats[$numberFormatIndex]];
2041 4
            } elseif (($code = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
2042
                // then we have built-in format code
2043 4
                $numberformat = ['code' => $code];
2044
            } else {
2045
                // we set the general format code
2046
                $numberformat = ['code' => 'General'];
2047
            }
2048 4
            $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
2049
2050
            // offset:  4; size: 2; XF type, cell protection, and parent style XF
2051
            // bit 2-0; mask 0x0007; XF_TYPE_PROT
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2052 4
            $xfTypeProt = self::getInt2d($recordData, 4);
2053
            // bit 0; mask 0x01; 1 = cell is locked
2054 4
            $isLocked = (0x01 & $xfTypeProt) >> 0;
2055 4
            $objStyle->getProtection()->setLocked($isLocked ? \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_INHERIT : \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2056
2057
            // bit 1; mask 0x02; 1 = Formula is hidden
2058 4
            $isHidden = (0x02 & $xfTypeProt) >> 1;
2059 4
            $objStyle->getProtection()->setHidden($isHidden ? \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_PROTECTED : \PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2060
2061
            // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
2062 4
            $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
2063
2064
            // offset:  6; size: 1; Alignment and text break
2065
            // bit 2-0, mask 0x07; horizontal alignment
2066 4
            $horAlign = (0x07 & ord($recordData{6})) >> 0;
2067
            switch ($horAlign) {
2068 4
                case 0:
2069 4
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL);
2070 4
                    break;
2071 2
                case 1:
2072 2
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
2073 2
                    break;
2074 2
                case 2:
2075
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
2076
                    break;
2077 2
                case 3:
2078 2
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT);
2079 2
                    break;
2080 2
                case 4:
2081
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_FILL);
2082
                    break;
2083 2
                case 5:
2084 2
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY);
2085 2
                    break;
2086
                case 6:
2087
                    $objStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS);
2088
                    break;
2089
            }
2090
            // bit 3, mask 0x08; wrap text
2091 4
            $wrapText = (0x08 & ord($recordData{6})) >> 3;
2092
            switch ($wrapText) {
2093 4
                case 0:
2094 4
                    $objStyle->getAlignment()->setWrapText(false);
2095 4
                    break;
2096 2
                case 1:
2097 2
                    $objStyle->getAlignment()->setWrapText(true);
2098 2
                    break;
2099
            }
2100
            // bit 6-4, mask 0x70; vertical alignment
2101 4
            $vertAlign = (0x70 & ord($recordData{6})) >> 4;
2102
            switch ($vertAlign) {
2103 4
                case 0:
2104
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP);
2105
                    break;
2106 4
                case 1:
2107 2
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
2108 2
                    break;
2109 4
                case 2:
2110 4
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM);
2111 4
                    break;
2112
                case 3:
2113
                    $objStyle->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_JUSTIFY);
2114
                    break;
2115
            }
2116
2117 4
            if ($this->version == self::XLS_BIFF8) {
2118
                // offset:  7; size: 1; XF_ROTATION: Text rotation angle
2119 4
                $angle = ord($recordData{7});
2120 4
                $rotation = 0;
2121 4
                if ($angle <= 90) {
2122 4
                    $rotation = $angle;
2123
                } elseif ($angle <= 180) {
2124
                    $rotation = 90 - $angle;
2125
                } elseif ($angle == 255) {
2126
                    $rotation = -165;
2127
                }
2128 4
                $objStyle->getAlignment()->setTextRotation($rotation);
2129
2130
                // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
2131
                // bit: 3-0; mask: 0x0F; indent level
2132 4
                $indent = (0x0F & ord($recordData{8})) >> 0;
2133 4
                $objStyle->getAlignment()->setIndent($indent);
2134
2135
                // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
2136 4
                $shrinkToFit = (0x10 & ord($recordData{8})) >> 4;
2137
                switch ($shrinkToFit) {
2138 4
                    case 0:
2139 4
                        $objStyle->getAlignment()->setShrinkToFit(false);
2140 4
                        break;
2141 1
                    case 1:
2142 1
                        $objStyle->getAlignment()->setShrinkToFit(true);
2143 1
                        break;
2144
                }
2145
2146
                // offset:  9; size: 1; Flags used for attribute groups
2147
2148
                // offset: 10; size: 4; Cell border lines and background area
2149
                // bit: 3-0; mask: 0x0000000F; left style
2150 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...
2151 4
                    $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
2152
                }
2153
                // bit: 7-4; mask: 0x000000F0; right style
2154 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...
2155 4
                    $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
2156
                }
2157
                // bit: 11-8; mask: 0x00000F00; top style
2158 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...
2159 4
                    $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
2160
                }
2161
                // bit: 15-12; mask: 0x0000F000; bottom style
2162 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...
2163 4
                    $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
2164
                }
2165
                // bit: 22-16; mask: 0x007F0000; left color
2166 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...
2167
2168
                // bit: 29-23; mask: 0x3F800000; right color
2169 4
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2170
2171
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2172 4
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2173
2174
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2175 4
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2176
2177 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...
2178 4
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_NONE);
2179
                } elseif ($diagonalUp == true && $diagonalDown == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

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

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

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

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

Loading history...
2184
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_BOTH);
2185
                }
2186
2187
                // offset: 14; size: 4;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2188
                // bit: 6-0; mask: 0x0000007F; top color
2189 4
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2190
2191
                // bit: 13-7; mask: 0x00003F80; bottom color
2192 4
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2193
2194
                // bit: 20-14; mask: 0x001FC000; diagonal color
2195 4
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2196
2197
                // bit: 24-21; mask: 0x01E00000; diagonal style
2198 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...
2199 4
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2200
                }
2201
2202
                // bit: 31-26; mask: 0xFC000000 fill pattern
2203 4
                if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2204 4
                    $objStyle->getFill()->setFillType($fillType);
2205
                }
2206
                // offset: 18; size: 2; pattern and background colour
2207
                // bit: 6-0; mask: 0x007F; color index for pattern color
2208 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...
2209
2210
                // bit: 13-7; mask: 0x3F80; color index for pattern background
2211 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...
2212
            } else {
2213
                // BIFF5
2214
2215
                // offset: 7; size: 1; Text orientation and flags
2216
                $orientationAndFlags = ord($recordData{7});
2217
2218
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2219
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2220
                switch ($xfOrientation) {
2221
                    case 0:
2222
                        $objStyle->getAlignment()->setTextRotation(0);
2223
                        break;
2224
                    case 1:
2225
                        $objStyle->getAlignment()->setTextRotation(-165);
2226
                        break;
2227
                    case 2:
2228
                        $objStyle->getAlignment()->setTextRotation(90);
2229
                        break;
2230
                    case 3:
2231
                        $objStyle->getAlignment()->setTextRotation(-90);
2232
                        break;
2233
                }
2234
2235
                // offset: 8; size: 4; cell border lines and background area
2236
                $borderAndBackground = self::getInt4d($recordData, 8);
2237
2238
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2239
                $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

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

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

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

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

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

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

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

These else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
2494
                // user-defined; not supported by PhpSpreadsheet
2495
            }
2496
        }
2497 4
    }
2498
2499
    /**
2500
     * Read PALETTE record
2501
     */
2502 2
    private function readPalette()
2503
    {
2504 2
        $length = self::getInt2d($this->data, $this->pos + 2);
2505 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2506
2507
        // move stream pointer to next record
2508 2
        $this->pos += 4 + $length;
2509
2510 2
        if (!$this->readDataOnly) {
2511
            // offset: 0; size: 2; number of following colors
2512 2
            $nm = self::getInt2d($recordData, 0);
2513
2514
            // list of RGB colors
2515 2
            for ($i = 0; $i < $nm; ++$i) {
2516 2
                $rgb = substr($recordData, 2 + 4 * $i, 4);
2517 2
                $this->palette[] = self::readRGB($rgb);
2518
            }
2519
        }
2520 2
    }
2521
2522
    /**
2523
     * SHEET
2524
     *
2525
     * This record is  located in the  Workbook Globals
2526
     * Substream  and represents a sheet inside the workbook.
2527
     * One SHEET record is written for each sheet. It stores the
2528
     * sheet name and a stream offset to the BOF record of the
2529
     * respective Sheet Substream within the Workbook Stream.
2530
     *
2531
     * --    "OpenOffice.org's Documentation of the Microsoft
2532
     *         Excel File Format"
2533
     */
2534 4
    private function readSheet()
2535
    {
2536 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2537 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2538
2539
        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
2540
        // NOTE: not encrypted
2541 4
        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
2542
2543
        // move stream pointer to next record
2544 4
        $this->pos += 4 + $length;
2545
2546
        // offset: 4; size: 1; sheet state
2547 4
        switch (ord($recordData{4})) {
2548 4
            case 0x00:
2549 4
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2550 4
                break;
2551
            case 0x01:
2552
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_HIDDEN;
2553
                break;
2554
            case 0x02:
2555
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_VERYHIDDEN;
2556
                break;
2557
            default:
2558
                $sheetState = \PhpOffice\PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2559
                break;
2560
        }
2561
2562
        // offset: 5; size: 1; sheet type
2563 4
        $sheetType = ord($recordData{5});
2564
2565
        // offset: 6; size: var; sheet name
2566 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...
2567 4
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2568 4
            $rec_name = $string['value'];
2569
        } elseif ($this->version == self::XLS_BIFF7) {
2570
            $string = $this->readByteStringShort(substr($recordData, 6));
2571
            $rec_name = $string['value'];
2572
        }
2573
2574 4
        $this->sheets[] = [
2575 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...
2576 4
            'offset' => $rec_offset,
2577 4
            'sheetState' => $sheetState,
2578 4
            'sheetType' => $sheetType,
2579
        ];
2580 4
    }
2581
2582
    /**
2583
     * Read EXTERNALBOOK record
2584
     */
2585 3
    private function readExternalBook()
2586
    {
2587 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2588 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2589
2590
        // move stream pointer to next record
2591 3
        $this->pos += 4 + $length;
2592
2593
        // offset within record data
2594 3
        $offset = 0;
2595
2596
        // there are 4 types of records
2597 3
        if (strlen($recordData) > 4) {
2598
            // external reference
2599
            // offset: 0; size: 2; number of sheet names ($nm)
2600
            $nm = self::getInt2d($recordData, 0);
2601
            $offset += 2;
2602
2603
            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
2604
            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2605
            $offset += $encodedUrlString['size'];
2606
2607
            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
2608
            $externalSheetNames = [];
2609
            for ($i = 0; $i < $nm; ++$i) {
2610
                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2611
                $externalSheetNames[] = $externalSheetNameString['value'];
2612
                $offset += $externalSheetNameString['size'];
2613
            }
2614
2615
            // store the record data
2616
            $this->externalBooks[] = [
2617
                'type' => 'external',
2618
                'encodedUrl' => $encodedUrlString['value'],
2619
                'externalSheetNames' => $externalSheetNames,
2620
            ];
2621 3
        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
2622
            // internal reference
2623
            // offset: 0; size: 2; number of sheet in this document
2624
            // offset: 2; size: 2; 0x01 0x04
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2625 3
            $this->externalBooks[] = [
2626 3
                'type' => 'internal',
2627
            ];
2628
        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
2629
            // add-in function
2630
            // offset: 0; size: 2; 0x0001
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2631
            $this->externalBooks[] = [
2632
                'type' => 'addInFunction',
2633
            ];
2634
        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
2635
            // DDE links, OLE links
2636
            // offset: 0; size: 2; 0x0000
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2637
            // offset: 2; size: var; encoded source document name
2638
            $this->externalBooks[] = [
2639
                'type' => 'DDEorOLE',
2640
            ];
2641
        }
2642 3
    }
2643
2644
    /**
2645
     * Read EXTERNNAME record.
2646
     */
2647
    private function readExternName()
2648
    {
2649
        $length = self::getInt2d($this->data, $this->pos + 2);
2650
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2651
2652
        // move stream pointer to next record
2653
        $this->pos += 4 + $length;
2654
2655
        // external sheet references provided for named cells
2656
        if ($this->version == self::XLS_BIFF8) {
2657
            // offset: 0; size: 2; options
2658
            $options = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$options is not used, you could remove the assignment.

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

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

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

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

Loading history...
2659
2660
            // offset: 2; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2661
2662
            // offset: 4; size: 2; not used
2663
2664
            // offset: 6; size: var
2665
            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2666
2667
            // offset: var; size: var; formula data
2668
            $offset = 6 + $nameString['size'];
2669
            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
2670
2671
            $this->externalNames[] = [
2672
                'name' => $nameString['value'],
2673
                'formula' => $formula,
2674
            ];
2675
        }
2676
    }
2677
2678
    /**
2679
     * Read EXTERNSHEET record
2680
     */
2681 3
    private function readExternSheet()
2682
    {
2683 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2684 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2685
2686
        // move stream pointer to next record
2687 3
        $this->pos += 4 + $length;
2688
2689
        // external sheet references provided for named cells
2690 3
        if ($this->version == self::XLS_BIFF8) {
2691
            // offset: 0; size: 2; number of following ref structures
2692 3
            $nm = self::getInt2d($recordData, 0);
2693 3
            for ($i = 0; $i < $nm; ++$i) {
2694 3
                $this->ref[] = [
2695
                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
2696 3
                    'externalBookIndex' => self::getInt2d($recordData, 2 + 6 * $i),
2697
                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
2698 3
                    'firstSheetIndex' => self::getInt2d($recordData, 4 + 6 * $i),
2699
                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
2700 3
                    'lastSheetIndex' => self::getInt2d($recordData, 6 + 6 * $i),
2701
                ];
2702
            }
2703
        }
2704 3
    }
2705
2706
    /**
2707
     * DEFINEDNAME
2708
     *
2709
     * This record is part of a Link Table. It contains the name
2710
     * and the token array of an internal defined name. Token
2711
     * arrays of defined names contain tokens with aberrant
2712
     * token classes.
2713
     *
2714
     * --    "OpenOffice.org's Documentation of the Microsoft
2715
     *         Excel File Format"
2716
     */
2717 1
    private function readDefinedName()
2718
    {
2719 1
        $length = self::getInt2d($this->data, $this->pos + 2);
2720 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2721
2722
        // move stream pointer to next record
2723 1
        $this->pos += 4 + $length;
2724
2725 1
        if ($this->version == self::XLS_BIFF8) {
2726
            // retrieves named cells
2727
2728
            // offset: 0; size: 2; option flags
2729 1
            $opts = self::getInt2d($recordData, 0);
2730
2731
            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
2732 1
            $isBuiltInName = (0x0020 & $opts) >> 5;
2733
2734
            // offset: 2; size: 1; keyboard shortcut
2735
2736
            // offset: 3; size: 1; length of the name (character count)
2737 1
            $nlen = ord($recordData{3});
2738
2739
            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
2740
            // note: there can also be additional data, this is not included in $flen
2741 1
            $flen = self::getInt2d($recordData, 4);
2742
2743
            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
2744 1
            $scope = self::getInt2d($recordData, 8);
2745
2746
            // offset: 14; size: var; Name (Unicode string without length field)
2747 1
            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2748
2749
            // offset: var; size: $flen; formula data
2750 1
            $offset = 14 + $string['size'];
2751 1
            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
2752
2753
            try {
2754 1
                $formula = $this->getFormulaFromStructure($formulaStructure);
2755
            } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
2756
                $formula = '';
2757
            }
2758
2759 1
            $this->definedname[] = [
2760 1
                'isBuiltInName' => $isBuiltInName,
2761 1
                'name' => $string['value'],
2762 1
                'formula' => $formula,
2763 1
                'scope' => $scope,
2764
            ];
2765
        }
2766 1
    }
2767
2768
    /**
2769
     * Read MSODRAWINGGROUP record
2770
     */
2771 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...
2772
    {
2773 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...
2774
2775
        // get spliced record data
2776 3
        $splicedRecordData = $this->getSplicedRecordData();
2777 3
        $recordData = $splicedRecordData['recordData'];
2778
2779 3
        $this->drawingGroupData .= $recordData;
2780 3
    }
2781
2782
    /**
2783
     * SST - Shared String Table
2784
     *
2785
     * This record contains a list of all strings used anywhere
2786
     * in the workbook. Each string occurs only once. The
2787
     * workbook uses indexes into the list to reference the
2788
     * strings.
2789
     *
2790
     * --    "OpenOffice.org's Documentation of the Microsoft
2791
     *         Excel File Format"
2792
     **/
2793 4
    private function readSst()
2794
    {
2795
        // offset within (spliced) record data
2796 4
        $pos = 0;
2797
2798
        // get spliced record data
2799 4
        $splicedRecordData = $this->getSplicedRecordData();
2800
2801 4
        $recordData = $splicedRecordData['recordData'];
2802 4
        $spliceOffsets = $splicedRecordData['spliceOffsets'];
2803
2804
        // offset: 0; size: 4; total number of strings in the workbook
2805 4
        $pos += 4;
2806
2807
        // offset: 4; size: 4; number of following strings ($nm)
2808 4
        $nm = self::getInt4d($recordData, 4);
2809 4
        $pos += 4;
2810
2811
        // loop through the Unicode strings (16-bit length)
2812 4
        for ($i = 0; $i < $nm; ++$i) {
2813
            // number of characters in the Unicode string
2814 4
            $numChars = self::getInt2d($recordData, $pos);
2815 4
            $pos += 2;
2816
2817
            // option flags
2818 4
            $optionFlags = ord($recordData{$pos});
2819 4
            ++$pos;
2820
2821
            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
2822 4
            $isCompressed = (($optionFlags & 0x01) == 0);
2823
2824
            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
2825 4
            $hasAsian = (($optionFlags & 0x04) != 0);
2826
2827
            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
2828 4
            $hasRichText = (($optionFlags & 0x08) != 0);
2829
2830 4
            if ($hasRichText) {
2831
                // number of Rich-Text formatting runs
2832 2
                $formattingRuns = self::getInt2d($recordData, $pos);
2833 2
                $pos += 2;
2834
            }
2835
2836 4
            if ($hasAsian) {
2837
                // size of Asian phonetic setting
2838
                $extendedRunLength = self::getInt4d($recordData, $pos);
2839
                $pos += 4;
2840
            }
2841
2842
            // expected byte length of character array if not split
2843 4
            $len = ($isCompressed) ? $numChars : $numChars * 2;
2844
2845
            // look up limit position
2846 4
            foreach ($spliceOffsets as $spliceOffset) {
2847
                // it can happen that the string is empty, therefore we need
2848
                // <= and not just <
2849 4
                if ($pos <= $spliceOffset) {
2850 4
                    $limitpos = $spliceOffset;
2851 4
                    break;
2852
                }
2853
            }
2854
2855 4
            if ($pos + $len <= $limitpos) {
2856
                // character array is not split between records
2857
2858 4
                $retstr = substr($recordData, $pos, $len);
2859 4
                $pos += $len;
2860
            } else {
2861
                // character array is split between records
2862
2863
                // first part of character array
2864
                $retstr = substr($recordData, $pos, $limitpos - $pos);
0 ignored issues
show
Bug introduced by
The variable $limitpos does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2865
2866
                $bytesRead = $limitpos - $pos;
2867
2868
                // remaining characters in Unicode string
2869
                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2870
2871
                $pos = $limitpos;
2872
2873
                // keep reading the characters
2874
                while ($charsLeft > 0) {
2875
                    // look up next limit position, in case the string span more than one continue record
2876
                    foreach ($spliceOffsets as $spliceOffset) {
2877
                        if ($pos < $spliceOffset) {
2878
                            $limitpos = $spliceOffset;
2879
                            break;
2880
                        }
2881
                    }
2882
2883
                    // repeated option flags
2884
                    // OpenOffice.org documentation 5.21
2885
                    $option = ord($recordData{$pos});
2886
                    ++$pos;
2887
2888
                    if ($isCompressed && ($option == 0)) {
2889
                        // 1st fragment compressed
2890
                        // this fragment compressed
2891
                        $len = min($charsLeft, $limitpos - $pos);
2892
                        $retstr .= substr($recordData, $pos, $len);
2893
                        $charsLeft -= $len;
2894
                        $isCompressed = true;
2895
                    } elseif (!$isCompressed && ($option != 0)) {
2896
                        // 1st fragment uncompressed
2897
                        // this fragment uncompressed
2898
                        $len = min($charsLeft * 2, $limitpos - $pos);
2899
                        $retstr .= substr($recordData, $pos, $len);
2900
                        $charsLeft -= $len / 2;
2901
                        $isCompressed = false;
2902
                    } elseif (!$isCompressed && ($option == 0)) {
2903
                        // 1st fragment uncompressed
2904
                        // this fragment compressed
2905
                        $len = min($charsLeft, $limitpos - $pos);
2906
                        for ($j = 0; $j < $len; ++$j) {
2907
                            $retstr .= $recordData{$pos + $j}
2908
                            . chr(0);
2909
                        }
2910
                        $charsLeft -= $len;
2911
                        $isCompressed = false;
2912
                    } else {
2913
                        // 1st fragment compressed
2914
                        // this fragment uncompressed
2915
                        $newstr = '';
2916
                        for ($j = 0; $j < strlen($retstr); ++$j) {
2917
                            $newstr .= $retstr[$j] . chr(0);
2918
                        }
2919
                        $retstr = $newstr;
2920
                        $len = min($charsLeft * 2, $limitpos - $pos);
2921
                        $retstr .= substr($recordData, $pos, $len);
2922
                        $charsLeft -= $len / 2;
2923
                        $isCompressed = false;
2924
                    }
2925
2926
                    $pos += $len;
2927
                }
2928
            }
2929
2930
            // convert to UTF-8
2931 4
            $retstr = self::encodeUTF16($retstr, $isCompressed);
2932
2933
            // read additional Rich-Text information, if any
2934 4
            $fmtRuns = [];
2935 4
            if ($hasRichText) {
2936
                // list of formatting runs
2937 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...
2938
                    // first formatted character; zero-based
2939 2
                    $charPos = self::getInt2d($recordData, $pos + $j * 4);
2940
2941
                    // index to font record
2942 2
                    $fontIndex = self::getInt2d($recordData, $pos + 2 + $j * 4);
2943
2944 2
                    $fmtRuns[] = [
2945 2
                        'charPos' => $charPos,
2946 2
                        'fontIndex' => $fontIndex,
2947
                    ];
2948
                }
2949 2
                $pos += 4 * $formattingRuns;
2950
            }
2951
2952
            // read additional Asian phonetics information, if any
2953 4
            if ($hasAsian) {
2954
                // For Asian phonetic settings, we skip the extended string data
2955
                $pos += $extendedRunLength;
0 ignored issues
show
Bug introduced by
The variable $extendedRunLength does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2956
            }
2957
2958
            // store the shared sting
2959 4
            $this->sst[] = [
2960 4
                'value' => $retstr,
2961 4
                'fmtRuns' => $fmtRuns,
2962
            ];
2963
        }
2964
2965
        // getSplicedRecordData() takes care of moving current position in data stream
2966 4
    }
2967
2968
    /**
2969
     * Read PRINTGRIDLINES record
2970
     */
2971 4
    private function readPrintGridlines()
2972
    {
2973 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2974 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2975
2976
        // move stream pointer to next record
2977 4
        $this->pos += 4 + $length;
2978
2979 4
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
2980
            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
2981 4
            $printGridlines = (bool) self::getInt2d($recordData, 0);
2982 4
            $this->phpSheet->setPrintGridlines($printGridlines);
2983
        }
2984 4
    }
2985
2986
    /**
2987
     * Read DEFAULTROWHEIGHT record
2988
     */
2989 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...
2990
    {
2991 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2992 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2993
2994
        // move stream pointer to next record
2995 3
        $this->pos += 4 + $length;
2996
2997
        // offset: 0; size: 2; option flags
2998
        // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2999 3
        $height = self::getInt2d($recordData, 2);
3000 3
        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3001 3
    }
3002
3003
    /**
3004
     * Read SHEETPR record
3005
     */
3006 4
    private function readSheetPr()
3007
    {
3008 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3009 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3010
3011
        // move stream pointer to next record
3012 4
        $this->pos += 4 + $length;
3013
3014
        // offset: 0; size: 2
3015
3016
        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
3017 4
        $isSummaryBelow = (0x0040 & self::getInt2d($recordData, 0)) >> 6;
3018 4
        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
3019
3020
        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
3021 4
        $isSummaryRight = (0x0080 & self::getInt2d($recordData, 0)) >> 7;
3022 4
        $this->phpSheet->setShowSummaryRight($isSummaryRight);
3023
3024
        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
3025
        // this corresponds to radio button setting in page setup dialog in Excel
3026 4
        $this->isFitToPages = (bool) ((0x0100 & self::getInt2d($recordData, 0)) >> 8);
3027 4
    }
3028
3029
    /**
3030
     * Read HORIZONTALPAGEBREAKS record
3031
     */
3032 View Code Duplication
    private function readHorizontalPageBreaks()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3033
    {
3034
        $length = self::getInt2d($this->data, $this->pos + 2);
3035
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3036
3037
        // move stream pointer to next record
3038
        $this->pos += 4 + $length;
3039
3040
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3041
            // offset: 0; size: 2; number of the following row index structures
3042
            $nm = self::getInt2d($recordData, 0);
3043
3044
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3045
            for ($i = 0; $i < $nm; ++$i) {
3046
                $r = self::getInt2d($recordData, 2 + 6 * $i);
3047
                $cf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3048
                $cl = self::getInt2d($recordData, 2 + 6 * $i + 4);
0 ignored issues
show
Unused Code introduced by
$cl is not used, you could remove the assignment.

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

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

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

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

Loading history...
3049
3050
                // not sure why two column indexes are necessary?
3051
                $this->phpSheet->setBreakByColumnAndRow($cf, $r, \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_ROW);
3052
            }
3053
        }
3054
    }
3055
3056
    /**
3057
     * Read VERTICALPAGEBREAKS record
3058
     */
3059 View Code Duplication
    private function readVerticalPageBreaks()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3060
    {
3061
        $length = self::getInt2d($this->data, $this->pos + 2);
3062
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3063
3064
        // move stream pointer to next record
3065
        $this->pos += 4 + $length;
3066
3067
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3068
            // offset: 0; size: 2; number of the following column index structures
3069
            $nm = self::getInt2d($recordData, 0);
3070
3071
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3072
            for ($i = 0; $i < $nm; ++$i) {
3073
                $c = self::getInt2d($recordData, 2 + 6 * $i);
3074
                $rf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3075
                $rl = self::getInt2d($recordData, 2 + 6 * $i + 4);
0 ignored issues
show
Unused Code introduced by
$rl is not used, you could remove the assignment.

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

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

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

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

Loading history...
3076
3077
                // not sure why two row indexes are necessary?
3078
                $this->phpSheet->setBreakByColumnAndRow($c, $rf, \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_COLUMN);
3079
            }
3080
        }
3081
    }
3082
3083
    /**
3084
     * Read HEADER record
3085
     */
3086 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...
3087
    {
3088 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3089 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3090
3091
        // move stream pointer to next record
3092 4
        $this->pos += 4 + $length;
3093
3094 4
        if (!$this->readDataOnly) {
3095
            // offset: 0; size: var
3096
            // realized that $recordData can be empty even when record exists
3097 4
            if ($recordData) {
3098 3
                if ($this->version == self::XLS_BIFF8) {
3099 3
                    $string = self::readUnicodeStringLong($recordData);
3100
                } else {
3101
                    $string = $this->readByteStringShort($recordData);
3102
                }
3103
3104 3
                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
3105 3
                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
3106
            }
3107
        }
3108 4
    }
3109
3110
    /**
3111
     * Read FOOTER record
3112
     */
3113 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...
3114
    {
3115 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3116 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3117
3118
        // move stream pointer to next record
3119 4
        $this->pos += 4 + $length;
3120
3121 4
        if (!$this->readDataOnly) {
3122
            // offset: 0; size: var
3123
            // realized that $recordData can be empty even when record exists
3124 4
            if ($recordData) {
3125 3
                if ($this->version == self::XLS_BIFF8) {
3126 3
                    $string = self::readUnicodeStringLong($recordData);
3127
                } else {
3128
                    $string = $this->readByteStringShort($recordData);
3129
                }
3130 3
                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
3131 3
                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
3132
            }
3133
        }
3134 4
    }
3135
3136
    /**
3137
     * Read HCENTER record
3138
     */
3139 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...
3140
    {
3141 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3142 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3143
3144
        // move stream pointer to next record
3145 4
        $this->pos += 4 + $length;
3146
3147 4
        if (!$this->readDataOnly) {
3148
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3149 4
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3150
3151 4
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3152
        }
3153 4
    }
3154
3155
    /**
3156
     * Read VCENTER record
3157
     */
3158 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...
3159
    {
3160 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3161 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3162
3163
        // move stream pointer to next record
3164 4
        $this->pos += 4 + $length;
3165
3166 4
        if (!$this->readDataOnly) {
3167
            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
3168 4
            $isVerticalCentered = (bool) self::getInt2d($recordData, 0);
3169
3170 4
            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3171
        }
3172 4
    }
3173
3174
    /**
3175
     * Read LEFTMARGIN record
3176
     */
3177 4
    private function readLeftMargin()
3178
    {
3179 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3180 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3181
3182
        // move stream pointer to next record
3183 4
        $this->pos += 4 + $length;
3184
3185 4
        if (!$this->readDataOnly) {
3186
            // offset: 0; size: 8
3187 4
            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3188
        }
3189 4
    }
3190
3191
    /**
3192
     * Read RIGHTMARGIN record
3193
     */
3194 4
    private function readRightMargin()
3195
    {
3196 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3197 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3198
3199
        // move stream pointer to next record
3200 4
        $this->pos += 4 + $length;
3201
3202 4
        if (!$this->readDataOnly) {
3203
            // offset: 0; size: 8
3204 4
            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3205
        }
3206 4
    }
3207
3208
    /**
3209
     * Read TOPMARGIN record
3210
     */
3211 4
    private function readTopMargin()
3212
    {
3213 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3214 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3215
3216
        // move stream pointer to next record
3217 4
        $this->pos += 4 + $length;
3218
3219 4
        if (!$this->readDataOnly) {
3220
            // offset: 0; size: 8
3221 4
            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3222
        }
3223 4
    }
3224
3225
    /**
3226
     * Read BOTTOMMARGIN record
3227
     */
3228 4
    private function readBottomMargin()
3229
    {
3230 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3231 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3232
3233
        // move stream pointer to next record
3234 4
        $this->pos += 4 + $length;
3235
3236 4
        if (!$this->readDataOnly) {
3237
            // offset: 0; size: 8
3238 4
            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3239
        }
3240 4
    }
3241
3242
    /**
3243
     * Read PAGESETUP record
3244
     */
3245 4
    private function readPageSetup()
3246
    {
3247 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3248 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3249
3250
        // move stream pointer to next record
3251 4
        $this->pos += 4 + $length;
3252
3253 4
        if (!$this->readDataOnly) {
3254
            // offset: 0; size: 2; paper size
3255 4
            $paperSize = self::getInt2d($recordData, 0);
3256
3257
            // offset: 2; size: 2; scaling factor
3258 4
            $scale = self::getInt2d($recordData, 2);
3259
3260
            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
3261 4
            $fitToWidth = self::getInt2d($recordData, 6);
3262
3263
            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
3264 4
            $fitToHeight = self::getInt2d($recordData, 8);
3265
3266
            // offset: 10; size: 2; option flags
3267
3268
            // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3269 4
            $isPortrait = (0x0002 & self::getInt2d($recordData, 10)) >> 1;
3270
3271
            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
3272
            // when this bit is set, do not use flags for those properties
3273 4
            $isNotInit = (0x0004 & self::getInt2d($recordData, 10)) >> 2;
3274
3275 4
            if (!$isNotInit) {
3276 4
                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3277
                switch ($isPortrait) {
3278 4
                    case 0:
3279 2
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
3280 2
                        break;
3281 4
                    case 1:
3282 4
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
3283 4
                        break;
3284
                }
3285
3286 4
                $this->phpSheet->getPageSetup()->setScale($scale, false);
3287 4
                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
3288 4
                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
3289 4
                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
3290
            }
3291
3292
            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
3293 4
            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3294 4
            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3295
3296
            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
3297 4
            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3298 4
            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3299
        }
3300 4
    }
3301
3302
    /**
3303
     * PROTECT - Sheet protection (BIFF2 through BIFF8)
3304
     *   if this record is omitted, then it also means no sheet protection
3305
     */
3306 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...
3307
    {
3308 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3309 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3310
3311
        // move stream pointer to next record
3312 1
        $this->pos += 4 + $length;
3313
3314 1
        if ($this->readDataOnly) {
3315
            return;
3316
        }
3317
3318
        // offset: 0; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3319
3320
        // bit 0, mask 0x01; 1 = sheet is protected
3321 1
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3322 1
        $this->phpSheet->getProtection()->setSheet((bool) $bool);
3323 1
    }
3324
3325
    /**
3326
     * SCENPROTECT
3327
     */
3328 View Code Duplication
    private function readScenProtect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3329
    {
3330
        $length = self::getInt2d($this->data, $this->pos + 2);
3331
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3332
3333
        // move stream pointer to next record
3334
        $this->pos += 4 + $length;
3335
3336
        if ($this->readDataOnly) {
3337
            return;
3338
        }
3339
3340
        // offset: 0; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3341
3342
        // bit: 0, mask 0x01; 1 = scenarios are protected
3343
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3344
3345
        $this->phpSheet->getProtection()->setScenarios((bool) $bool);
3346
    }
3347
3348
    /**
3349
     * OBJECTPROTECT
3350
     */
3351 View Code Duplication
    private function readObjectProtect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3352
    {
3353
        $length = self::getInt2d($this->data, $this->pos + 2);
3354
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3355
3356
        // move stream pointer to next record
3357
        $this->pos += 4 + $length;
3358
3359
        if ($this->readDataOnly) {
3360
            return;
3361
        }
3362
3363
        // offset: 0; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3364
3365
        // bit: 0, mask 0x01; 1 = objects are protected
3366
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3367
3368
        $this->phpSheet->getProtection()->setObjects((bool) $bool);
3369
    }
3370
3371
    /**
3372
     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
3373
     */
3374
    private function readPassword()
3375
    {
3376
        $length = self::getInt2d($this->data, $this->pos + 2);
3377
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3378
3379
        // move stream pointer to next record
3380
        $this->pos += 4 + $length;
3381
3382
        if (!$this->readDataOnly) {
3383
            // offset: 0; size: 2; 16-bit hash value of password
3384
            $password = strtoupper(dechex(self::getInt2d($recordData, 0))); // the hashed password
3385
            $this->phpSheet->getProtection()->setPassword($password, true);
3386
        }
3387
    }
3388
3389
    /**
3390
     * Read DEFCOLWIDTH record
3391
     */
3392 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...
3393
    {
3394 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3395 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3396
3397
        // move stream pointer to next record
3398 4
        $this->pos += 4 + $length;
3399
3400
        // offset: 0; size: 2; default column width
3401 4
        $width = self::getInt2d($recordData, 0);
3402 4
        if ($width != 8) {
3403
            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3404
        }
3405 4
    }
3406
3407
    /**
3408
     * Read COLINFO record
3409
     */
3410 3
    private function readColInfo()
3411
    {
3412 3
        $length = self::getInt2d($this->data, $this->pos + 2);
3413 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3414
3415
        // move stream pointer to next record
3416 3
        $this->pos += 4 + $length;
3417
3418 3
        if (!$this->readDataOnly) {
3419
            // offset: 0; size: 2; index to first column in range
3420 3
            $fc = self::getInt2d($recordData, 0); // first column index
3421
3422
            // offset: 2; size: 2; index to last column in range
3423 3
            $lc = self::getInt2d($recordData, 2); // first column index
3424
3425
            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
3426 3
            $width = self::getInt2d($recordData, 4);
3427
3428
            // offset: 6; size: 2; index to XF record for default column formatting
3429 3
            $xfIndex = self::getInt2d($recordData, 6);
3430
3431
            // offset: 8; size: 2; option flags
3432
            // bit: 0; mask: 0x0001; 1= columns are hidden
3433 3
            $isHidden = (0x0001 & self::getInt2d($recordData, 8)) >> 0;
3434
3435
            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
3436 3
            $level = (0x0700 & self::getInt2d($recordData, 8)) >> 8;
3437
3438
            // bit: 12; mask: 0x1000; 1 = collapsed
3439 3
            $isCollapsed = (0x1000 & self::getInt2d($recordData, 8)) >> 12;
3440
3441
            // offset: 10; size: 2; not used
3442
3443 3
            for ($i = $fc; $i <= $lc; ++$i) {
3444 3
                if ($lc == 255 || $lc == 256) {
3445
                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3446
                    break;
3447
                }
3448 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
3449 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
3450 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
3451 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
3452 3
                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3453
            }
3454
        }
3455 3
    }
3456
3457
    /**
3458
     * ROW
3459
     *
3460
     * This record contains the properties of a single row in a
3461
     * sheet. Rows and cells in a sheet are divided into blocks
3462
     * of 32 rows.
3463
     *
3464
     * --    "OpenOffice.org's Documentation of the Microsoft
3465
     *         Excel File Format"
3466
     */
3467 3
    private function readRow()
3468
    {
3469 3
        $length = self::getInt2d($this->data, $this->pos + 2);
3470 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3471
3472
        // move stream pointer to next record
3473 3
        $this->pos += 4 + $length;
3474
3475 3
        if (!$this->readDataOnly) {
3476
            // offset: 0; size: 2; index of this row
3477 3
            $r = self::getInt2d($recordData, 0);
3478
3479
            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
3480
3481
            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
3482
3483
            // offset: 6; size: 2;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3484
3485
            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
3486 3
            $height = (0x7FFF & self::getInt2d($recordData, 6)) >> 0;
3487
3488
            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
3489 3
            $useDefaultHeight = (0x8000 & self::getInt2d($recordData, 6)) >> 15;
3490
3491 3
            if (!$useDefaultHeight) {
3492 3
                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
3493
            }
3494
3495
            // offset: 8; size: 2; not used
3496
3497
            // offset: 10; size: 2; not used in BIFF5-BIFF8
3498
3499
            // offset: 12; size: 4; option flags and default row formatting
3500
3501
            // bit: 2-0: mask: 0x00000007; outline level of the row
3502 3
            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3503 3
            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
3504
3505
            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
3506 3
            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
3507 3
            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
3508
3509
            // bit: 5; mask: 0x00000020; 1 = row is hidden
3510 3
            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3511 3
            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
3512
3513
            // bit: 7; mask: 0x00000080; 1 = row has explicit format
3514 3
            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3515
3516
            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
3517 3
            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3518
3519 3
            if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
3520
                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3521
            }
3522
        }
3523 3
    }
3524
3525
    /**
3526
     * Read RK record
3527
     * This record represents a cell that contains an RK value
3528
     * (encoded integer or floating-point value). If a
3529
     * floating-point value cannot be encoded to an RK value,
3530
     * a NUMBER record will be written. This record replaces the
3531
     * record INTEGER written in BIFF2.
3532
     *
3533
     * --    "OpenOffice.org's Documentation of the Microsoft
3534
     *         Excel File Format"
3535
     */
3536 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...
3537
    {
3538 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3539 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3540
3541
        // move stream pointer to next record
3542 1
        $this->pos += 4 + $length;
3543
3544
        // offset: 0; size: 2; index to row
3545 1
        $row = self::getInt2d($recordData, 0);
3546
3547
        // offset: 2; size: 2; index to column
3548 1
        $column = self::getInt2d($recordData, 2);
3549 1
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3550
3551
        // Read cell?
3552 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3553
            // offset: 4; size: 2; index to XF record
3554 1
            $xfIndex = self::getInt2d($recordData, 4);
3555
3556
            // offset: 6; size: 4; RK value
3557 1
            $rknum = self::getInt4d($recordData, 6);
3558 1
            $numValue = self::getIEEE754($rknum);
3559
3560 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3561 1
            if (!$this->readDataOnly) {
3562
                // add style information
3563 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3564
            }
3565
3566
            // add cell
3567 1
            $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3568
        }
3569 1
    }
3570
3571
    /**
3572
     * Read LABELSST record
3573
     * This record represents a cell that contains a string. It
3574
     * replaces the LABEL record and RSTRING record used in
3575
     * BIFF2-BIFF5.
3576
     *
3577
     * --    "OpenOffice.org's Documentation of the Microsoft
3578
     *         Excel File Format"
3579
     */
3580 4
    private function readLabelSst()
3581
    {
3582 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3583 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3584
3585
        // move stream pointer to next record
3586 4
        $this->pos += 4 + $length;
3587
3588
        // offset: 0; size: 2; index to row
3589 4
        $row = self::getInt2d($recordData, 0);
3590
3591
        // offset: 2; size: 2; index to column
3592 4
        $column = self::getInt2d($recordData, 2);
3593 4
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3594
3595 4
        $emptyCell = true;
3596
        // Read cell?
3597 4
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3598
            // offset: 4; size: 2; index to XF record
3599 4
            $xfIndex = self::getInt2d($recordData, 4);
3600
3601
            // offset: 6; size: 4; index to SST record
3602 4
            $index = self::getInt4d($recordData, 6);
3603
3604
            // add cell
3605 4
            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
3606
                // then we should treat as rich text
3607 2
                $richText = new \PhpOffice\PhpSpreadsheet\RichText();
3608 2
                $charPos = 0;
3609 2
                $sstCount = count($this->sst[$index]['fmtRuns']);
3610 2
                for ($i = 0; $i <= $sstCount; ++$i) {
3611 2
                    if (isset($fmtRuns[$i])) {
3612 2
                        $text = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
3613 2
                        $charPos = $fmtRuns[$i]['charPos'];
3614
                    } else {
3615 2
                        $text = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, \PhpOffice\PhpSpreadsheet\Shared\StringHelper::countCharacters($this->sst[$index]['value']));
3616
                    }
3617
3618 2
                    if (\PhpOffice\PhpSpreadsheet\Shared\StringHelper::countCharacters($text) > 0) {
3619 2
                        if ($i == 0) { // first text run, no style
3620 1
                            $richText->createText($text);
3621
                        } else {
3622 2
                            $textRun = $richText->createTextRun($text);
3623 2
                            if (isset($fmtRuns[$i - 1])) {
3624 2
                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
3625 2
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
3626
                                } else {
3627
                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
3628
                                    // check the OpenOffice documentation of the FONT record
3629 2
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
3630
                                }
3631 2
                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
3632
                            }
3633
                        }
3634
                    }
3635
                }
3636 2
                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
3637 2
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3638 2
                    $cell->setValueExplicit($richText, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3639 2
                    $emptyCell = false;
3640
                }
3641
            } else {
3642 4
                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
3643 4
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3644 4
                    $cell->setValueExplicit($this->sst[$index]['value'], \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3645 4
                    $emptyCell = false;
3646
                }
3647
            }
3648
3649 4
            if (!$this->readDataOnly && !$emptyCell) {
3650
                // add style information
3651 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...
3652
            }
3653
        }
3654 4
    }
3655
3656
    /**
3657
     * Read MULRK record
3658
     * This record represents a cell range containing RK value
3659
     * cells. All cells are located in the same row.
3660
     *
3661
     * --    "OpenOffice.org's Documentation of the Microsoft
3662
     *         Excel File Format"
3663
     */
3664
    private function readMulRk()
3665
    {
3666
        $length = self::getInt2d($this->data, $this->pos + 2);
3667
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3668
3669
        // move stream pointer to next record
3670
        $this->pos += 4 + $length;
3671
3672
        // offset: 0; size: 2; index to row
3673
        $row = self::getInt2d($recordData, 0);
3674
3675
        // offset: 2; size: 2; index to first column
3676
        $colFirst = self::getInt2d($recordData, 2);
3677
3678
        // offset: var; size: 2; index to last column
3679
        $colLast = self::getInt2d($recordData, $length - 2);
3680
        $columns = $colLast - $colFirst + 1;
3681
3682
        // offset within record data
3683
        $offset = 4;
3684
3685
        for ($i = 0; $i < $columns; ++$i) {
3686
            $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colFirst + $i);
3687
3688
            // Read cell?
3689
            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3690
                // offset: var; size: 2; index to XF record
3691
                $xfIndex = self::getInt2d($recordData, $offset);
3692
3693
                // offset: var; size: 4; RK value
3694
                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3695
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3696
                if (!$this->readDataOnly) {
3697
                    // add style
3698
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3699
                }
3700
3701
                // add cell value
3702
                $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3703
            }
3704
3705
            $offset += 6;
3706
        }
3707
    }
3708
3709
    /**
3710
     * Read NUMBER record
3711
     * This record represents a cell that contains a
3712
     * floating-point value.
3713
     *
3714
     * --    "OpenOffice.org's Documentation of the Microsoft
3715
     *         Excel File Format"
3716
     */
3717 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...
3718
    {
3719 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3720 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3721
3722
        // move stream pointer to next record
3723 1
        $this->pos += 4 + $length;
3724
3725
        // offset: 0; size: 2; index to row
3726 1
        $row = self::getInt2d($recordData, 0);
3727
3728
        // offset: 2; size 2; index to column
3729 1
        $column = self::getInt2d($recordData, 2);
3730 1
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3731
3732
        // Read cell?
3733 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3734
            // offset 4; size: 2; index to XF record
3735 1
            $xfIndex = self::getInt2d($recordData, 4);
3736
3737 1
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3738
3739 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3740 1
            if (!$this->readDataOnly) {
3741
                // add cell style
3742 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3743
            }
3744
3745
            // add cell value
3746 1
            $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3747
        }
3748 1
    }
3749
3750
    /**
3751
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3752
     * This record contains the token array and the result of a
3753
     * formula cell.
3754
     *
3755
     * --    "OpenOffice.org's Documentation of the Microsoft
3756
     *         Excel File Format"
3757
     */
3758 2
    private function readFormula()
3759
    {
3760 2
        $length = self::getInt2d($this->data, $this->pos + 2);
3761 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3762
3763
        // move stream pointer to next record
3764 2
        $this->pos += 4 + $length;
3765
3766
        // offset: 0; size: 2; row index
3767 2
        $row = self::getInt2d($recordData, 0);
3768
3769
        // offset: 2; size: 2; col index
3770 2
        $column = self::getInt2d($recordData, 2);
3771 2
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3772
3773
        // offset: 20: size: variable; formula structure
3774 2
        $formulaStructure = substr($recordData, 20);
3775
3776
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3777 2
        $options = self::getInt2d($recordData, 14);
3778
3779
        // bit: 0; mask: 0x0001; 1 = recalculate always
3780
        // bit: 1; mask: 0x0002; 1 = calculate on open
3781
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3782 2
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3783
3784
        // WARNING:
3785
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3786
        // the formula data may be ordinary formula data, therefore we need to check
3787
        // explicitly for the tExp token (0x01)
3788 2
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01;
3789
3790 2
        if ($isPartOfSharedFormula) {
3791
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3792
            // get the base cell, grab tExp token
3793
            $baseRow = self::getInt2d($formulaStructure, 3);
3794
            $baseCol = self::getInt2d($formulaStructure, 5);
3795
            $this->_baseCell = \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...
3796
        }
3797
3798
        // Read cell?
3799 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3800 2
            if ($isPartOfSharedFormula) {
3801
                // formula is added to this cell after the sheet has been read
3802
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3803
            }
3804
3805
            // offset: 16: size: 4; not used
3806
3807
            // offset: 4; size: 2; XF index
3808 2
            $xfIndex = self::getInt2d($recordData, 4);
3809
3810
            // offset: 6; size: 8; result of the formula
3811 2
            if ((ord($recordData{6}) == 0) && (ord($recordData{12}) == 255) && (ord($recordData{13}) == 255)) {
3812
                // String formula. Result follows in appended STRING record
3813
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING;
3814
3815
                // read possible SHAREDFMLA record
3816
                $code = self::getInt2d($this->data, $this->pos);
3817
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3818
                    $this->readSharedFmla();
3819
                }
3820
3821
                // read STRING record
3822
                $value = $this->readString();
3823 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...
3824 2
                && (ord($recordData{12}) == 255)
3825 2
                && (ord($recordData{13}) == 255)) {
3826
                // Boolean formula. Result is in +2; 0=false, 1=true
3827
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
3828
                $value = (bool) ord($recordData{8});
3829 2
            } elseif ((ord($recordData{6}) == 2)
3830 2
                && (ord($recordData{12}) == 255)
3831 2
                && (ord($recordData{13}) == 255)) {
3832
                // Error formula. Error code is in +2
3833
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
3834
                $value = Xls\ErrorCode::lookup(ord($recordData{8}));
3835 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...
3836 2
                && (ord($recordData{12}) == 255)
3837 2
                && (ord($recordData{13}) == 255)) {
3838
                // Formula result is a null string
3839 1
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL;
3840 1
                $value = '';
3841
            } else {
3842
                // forumla result is a number, first 14 bytes like _NUMBER record
3843 2
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
3844 2
                $value = self::extractNumber(substr($recordData, 6, 8));
3845
            }
3846
3847 2
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3848 2
            if (!$this->readDataOnly) {
3849
                // add cell style
3850 2
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3851
            }
3852
3853
            // store the formula
3854 2
            if (!$isPartOfSharedFormula) {
3855
                // not part of shared formula
3856
                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
3857
                try {
3858 2
                    if ($this->version != self::XLS_BIFF8) {
3859
                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
3860
                    }
3861 2
                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
3862 2
                    $cell->setValueExplicit('=' . $formula, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
3863
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
3864 2
                    $cell->setValueExplicit($value, $dataType);
3865
                }
3866
            } else {
3867
                if ($this->version == self::XLS_BIFF8) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
3868
                    // do nothing at this point, formula id added later in the code
3869
                } else {
3870
                    $cell->setValueExplicit($value, $dataType);
3871
                }
3872
            }
3873
3874
            // store the cached calculated value
3875 2
            $cell->setCalculatedValue($value);
3876
        }
3877 2
    }
3878
3879
    /**
3880
     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
3881
     * which usually contains relative references.
3882
     * These will be used to construct the formula in each shared formula part after the sheet is read.
3883
     */
3884
    private function readSharedFmla()
3885
    {
3886
        $length = self::getInt2d($this->data, $this->pos + 2);
3887
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3888
3889
        // move stream pointer to next record
3890
        $this->pos += 4 + $length;
3891
3892
        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
3893
        $cellRange = substr($recordData, 0, 6);
3894
        $cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
0 ignored issues
show
Unused Code introduced by
$cellRange is not used, you could remove the assignment.

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

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

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

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

Loading history...
3895
3896
        // offset: 6, size: 1; not used
3897
3898
        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
3899
        $no = ord($recordData{7});
0 ignored issues
show
Unused Code introduced by
$no is not used, you could remove the assignment.

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

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

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

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

Loading history...
3900
3901
        // offset: 8, size: var; Binary token array of the shared formula
3902
        $formula = substr($recordData, 8);
3903
3904
        // at this point we only store the shared formula for later use
3905
        $this->sharedFormulas[$this->_baseCell] = $formula;
3906
    }
3907
3908
    /**
3909
     * Read a STRING record from current stream position and advance the stream pointer to next record
3910
     * This record is used for storing result from FORMULA record when it is a string, and
3911
     * it occurs directly after the FORMULA record
3912
     *
3913
     * @return string The string contents as UTF-8
3914
     */
3915
    private function readString()
3916
    {
3917
        $length = self::getInt2d($this->data, $this->pos + 2);
3918
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3919
3920
        // move stream pointer to next record
3921
        $this->pos += 4 + $length;
3922
3923
        if ($this->version == self::XLS_BIFF8) {
3924
            $string = self::readUnicodeStringLong($recordData);
3925
            $value = $string['value'];
3926
        } else {
3927
            $string = $this->readByteStringLong($recordData);
3928
            $value = $string['value'];
3929
        }
3930
3931
        return $value;
3932
    }
3933
3934
    /**
3935
     * Read BOOLERR record
3936
     * This record represents a Boolean value or error value
3937
     * cell.
3938
     *
3939
     * --    "OpenOffice.org's Documentation of the Microsoft
3940
     *         Excel File Format"
3941
     */
3942
    private function readBoolErr()
3943
    {
3944
        $length = self::getInt2d($this->data, $this->pos + 2);
3945
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3946
3947
        // move stream pointer to next record
3948
        $this->pos += 4 + $length;
3949
3950
        // offset: 0; size: 2; row index
3951
        $row = self::getInt2d($recordData, 0);
3952
3953
        // offset: 2; size: 2; column index
3954
        $column = self::getInt2d($recordData, 2);
3955
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3956
3957
        // Read cell?
3958
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3959
            // offset: 4; size: 2; index to XF record
3960
            $xfIndex = self::getInt2d($recordData, 4);
3961
3962
            // offset: 6; size: 1; the boolean value or error value
3963
            $boolErr = ord($recordData{6});
3964
3965
            // offset: 7; size: 1; 0=boolean; 1=error
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3966
            $isError = ord($recordData{7});
3967
3968
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3969
            switch ($isError) {
3970
                case 0: // boolean
3971
                    $value = (bool) $boolErr;
3972
3973
                    // add cell value
3974
                    $cell->setValueExplicit($value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL);
3975
                    break;
3976
                case 1: // error type
3977
                    $value = Xls\ErrorCode::lookup($boolErr);
3978
3979
                    // add cell value
3980
                    $cell->setValueExplicit($value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR);
3981
                    break;
3982
            }
3983
3984
            if (!$this->readDataOnly) {
3985
                // add cell style
3986
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3987
            }
3988
        }
3989
    }
3990
3991
    /**
3992
     * Read MULBLANK record
3993
     * This record represents a cell range of empty cells. All
3994
     * cells are located in the same row
3995
     *
3996
     * --    "OpenOffice.org's Documentation of the Microsoft
3997
     *         Excel File Format"
3998
     */
3999 1
    private function readMulBlank()
4000
    {
4001 1
        $length = self::getInt2d($this->data, $this->pos + 2);
4002 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4003
4004
        // move stream pointer to next record
4005 1
        $this->pos += 4 + $length;
4006
4007
        // offset: 0; size: 2; index to row
4008 1
        $row = self::getInt2d($recordData, 0);
4009
4010
        // offset: 2; size: 2; index to first column
4011 1
        $fc = self::getInt2d($recordData, 2);
4012
4013
        // offset: 4; size: 2 x nc; list of indexes to XF records
4014
        // add style information
4015 1
        if (!$this->readDataOnly && $this->readEmptyCells) {
4016 1
            for ($i = 0; $i < $length / 2 - 3; ++$i) {
4017 1
                $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fc + $i);
4018
4019
                // Read cell?
4020 1
                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4021 1
                    $xfIndex = self::getInt2d($recordData, 4 + 2 * $i);
4022 1
                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4023
                }
4024
            }
4025
        }
4026
4027
        // offset: 6; size 2; index to last column (not needed)
4028 1
    }
4029
4030
    /**
4031
     * Read LABEL record
4032
     * This record represents a cell that contains a string. In
4033
     * BIFF8 it is usually replaced by the LABELSST record.
4034
     * Excel still uses this record, if it copies unformatted
4035
     * text cells to the clipboard.
4036
     *
4037
     * --    "OpenOffice.org's Documentation of the Microsoft
4038
     *         Excel File Format"
4039
     */
4040
    private function readLabel()
4041
    {
4042
        $length = self::getInt2d($this->data, $this->pos + 2);
4043
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4044
4045
        // move stream pointer to next record
4046
        $this->pos += 4 + $length;
4047
4048
        // offset: 0; size: 2; index to row
4049
        $row = self::getInt2d($recordData, 0);
4050
4051
        // offset: 2; size: 2; index to column
4052
        $column = self::getInt2d($recordData, 2);
4053
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
4054
4055
        // Read cell?
4056
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4057
            // offset: 4; size: 2; XF index
4058
            $xfIndex = self::getInt2d($recordData, 4);
4059
4060
            // add cell value
4061
            // todo: what if string is very long? continue record
4062 View Code Duplication
            if ($this->version == self::XLS_BIFF8) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4063
                $string = self::readUnicodeStringLong(substr($recordData, 6));
4064
                $value = $string['value'];
4065
            } else {
4066
                $string = $this->readByteStringLong(substr($recordData, 6));
4067
                $value = $string['value'];
4068
            }
4069
            if ($this->readEmptyCells || trim($value) !== '') {
4070
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4071
                $cell->setValueExplicit($value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
4072
4073
                if (!$this->readDataOnly) {
4074
                    // add cell style
4075
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4076
                }
4077
            }
4078
        }
4079
    }
4080
4081
    /**
4082
     * Read BLANK record
4083
     */
4084 2
    private function readBlank()
4085
    {
4086 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4087 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4088
4089
        // move stream pointer to next record
4090 2
        $this->pos += 4 + $length;
4091
4092
        // offset: 0; size: 2; row index
4093 2
        $row = self::getInt2d($recordData, 0);
4094
4095
        // offset: 2; size: 2; col index
4096 2
        $col = self::getInt2d($recordData, 2);
4097 2
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($col);
4098
4099
        // Read cell?
4100 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4101
            // offset: 4; size: 2; XF index
4102 2
            $xfIndex = self::getInt2d($recordData, 4);
4103
4104
            // add style information
4105 2
            if (!$this->readDataOnly && $this->readEmptyCells) {
4106 2
                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4107
            }
4108
        }
4109 2
    }
4110
4111
    /**
4112
     * Read MSODRAWING record
4113
     */
4114 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...
4115
    {
4116 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...
4117
4118
        // get spliced record data
4119 2
        $splicedRecordData = $this->getSplicedRecordData();
4120 2
        $recordData = $splicedRecordData['recordData'];
4121
4122 2
        $this->drawingData .= $recordData;
4123 2
    }
4124
4125
    /**
4126
     * Read OBJ record
4127
     */
4128 2
    private function readObj()
4129
    {
4130 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4131 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4132
4133
        // move stream pointer to next record
4134 2
        $this->pos += 4 + $length;
4135
4136 2
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4137
            return;
4138
        }
4139
4140
        // recordData consists of an array of subrecords looking like this:
4141
        //    ft: 2 bytes; ftCmo type (0x15)
4142
        //    cb: 2 bytes; size in bytes of ftCmo data
4143
        //    ot: 2 bytes; Object Type
4144
        //    id: 2 bytes; Object id number
4145
        //    grbit: 2 bytes; Option Flags
4146
        //    data: var; subrecord data
4147
4148
        // for now, we are just interested in the second subrecord containing the object type
4149 2
        $ftCmoType = self::getInt2d($recordData, 0);
4150 2
        $cbCmoSize = self::getInt2d($recordData, 2);
4151 2
        $otObjType = self::getInt2d($recordData, 4);
4152 2
        $idObjID = self::getInt2d($recordData, 6);
4153 2
        $grbitOpts = self::getInt2d($recordData, 6);
4154
4155 2
        $this->objs[] = [
4156 2
            'ftCmoType' => $ftCmoType,
4157 2
            'cbCmoSize' => $cbCmoSize,
4158 2
            'otObjType' => $otObjType,
4159 2
            'idObjID' => $idObjID,
4160 2
            'grbitOpts' => $grbitOpts,
4161
        ];
4162 2
        $this->textObjRef = $idObjID;
4163 2
    }
4164
4165
    /**
4166
     * Read WINDOW2 record
4167
     */
4168 4
    private function readWindow2()
4169
    {
4170 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4171 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4172
4173
        // move stream pointer to next record
4174 4
        $this->pos += 4 + $length;
4175
4176
        // offset: 0; size: 2; option flags
4177 4
        $options = self::getInt2d($recordData, 0);
4178
4179
        // offset: 2; size: 2; index to first visible row
4180 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...
4181
4182
        // offset: 4; size: 2; index to first visible colum
4183 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...
4184 4
        if ($this->version === self::XLS_BIFF8) {
4185
            // offset:  8; size: 2; not used
4186
            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
4187
            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
4188
            // offset: 14; size: 4; not used
4189 4
            $zoomscaleInPageBreakPreview = self::getInt2d($recordData, 10);
4190 4
            if ($zoomscaleInPageBreakPreview === 0) {
4191 4
                $zoomscaleInPageBreakPreview = 60;
4192
            }
4193 4
            $zoomscaleInNormalView = self::getInt2d($recordData, 12);
4194 4
            if ($zoomscaleInNormalView === 0) {
4195 3
                $zoomscaleInNormalView = 100;
4196
            }
4197
        }
4198
4199
        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
4200 4
        $showGridlines = (bool) ((0x0002 & $options) >> 1);
4201 4
        $this->phpSheet->setShowGridlines($showGridlines);
4202
4203
        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
4204 4
        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4205 4
        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4206
4207
        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
4208 4
        $this->frozen = (bool) ((0x0008 & $options) >> 3);
4209
4210
        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
4211 4
        $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
4212
4213
        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
4214 4
        $isActive = (bool) ((0x0400 & $options) >> 10);
4215 4
        if ($isActive) {
4216 4
            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4217
        }
4218
4219
        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
4220 4
        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4221
4222
        //FIXME: set $firstVisibleRow and $firstVisibleColumn
4223
4224 4
        if ($this->phpSheet->getSheetView()->getView() !== \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT) {
4225
            //NOTE: this setting is inferior to page layout view(Excel2007-)
4226 4
            $view = $isPageBreakPreview ? \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_NORMAL;
4227 4
            $this->phpSheet->getSheetView()->setView($view);
4228 4
            if ($this->version === self::XLS_BIFF8) {
4229 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...
4230 4
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4231 4
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4232
            }
4233
        }
4234 4
    }
4235
4236
    /**
4237
     * Read PLV Record(Created by Excel2007 or upper)
4238
     */
4239 4
    private function readPageLayoutView()
4240
    {
4241 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4242 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4243
4244
        // move stream pointer to next record
4245 4
        $this->pos += 4 + $length;
4246
4247
        // offset: 0; size: 2; rt
4248
        //->ignore
4249 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...
4250
        // offset: 2; size: 2; grbitfr
4251
        //->ignore
4252 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...
4253
        // offset: 4; size: 8; reserved
4254
        //->ignore
4255
4256
        // offset: 12; size 2; zoom scale
4257 4
        $wScalePLV = self::getInt2d($recordData, 12);
4258
        // offset: 14; size 2; grbit
4259 4
        $grbit = self::getInt2d($recordData, 14);
4260
4261
        // decomprise grbit
4262 4
        $fPageLayoutView = $grbit & 0x01;
4263 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...
4264 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...
4265
4266 4
        if ($fPageLayoutView === 1) {
4267
            $this->phpSheet->getSheetView()->setView(\PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT);
4268
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4269
        }
4270
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4271 4
    }
4272
4273
    /**
4274
     * Read SCL record
4275
     */
4276
    private function readScl()
4277
    {
4278
        $length = self::getInt2d($this->data, $this->pos + 2);
4279
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4280
4281
        // move stream pointer to next record
4282
        $this->pos += 4 + $length;
4283
4284
        // offset: 0; size: 2; numerator of the view magnification
4285
        $numerator = self::getInt2d($recordData, 0);
4286
4287
        // offset: 2; size: 2; numerator of the view magnification
4288
        $denumerator = self::getInt2d($recordData, 2);
4289
4290
        // set the zoom scale (in percent)
4291
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4292
    }
4293
4294
    /**
4295
     * Read PANE record
4296
     */
4297
    private function readPane()
4298
    {
4299
        $length = self::getInt2d($this->data, $this->pos + 2);
4300
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4301
4302
        // move stream pointer to next record
4303
        $this->pos += 4 + $length;
4304
4305
        if (!$this->readDataOnly) {
4306
            // offset: 0; size: 2; position of vertical split
4307
            $px = self::getInt2d($recordData, 0);
4308
4309
            // offset: 2; size: 2; position of horizontal split
4310
            $py = self::getInt2d($recordData, 2);
4311
4312
            if ($this->frozen) {
4313
                // frozen panes
4314
                $this->phpSheet->freezePane(\PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($px) . ($py + 1));
4315
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

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

These else branches can be removed.

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

could be turned into

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

This is much more concise to read.

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

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

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

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

Loading history...
4434
            } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4435
                return;
4436
            }
4437
4438
            // offset: 8, size: 16; GUID of StdLink
4439
4440
            // offset: 24, size: 4; unknown value
4441
4442
            // offset: 28, size: 4; option flags
4443
            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
4444 2
            $isFileLinkOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 0;
4445
4446
            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
4447 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...
4448
4449
            // bit: 2 (and 4); mask: 0x00000014; 0 = no description
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4450 2
            $hasDesc = (0x00000014 & self::getInt2d($recordData, 28)) >> 2;
4451
4452
            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
4453 2
            $hasText = (0x00000008 & self::getInt2d($recordData, 28)) >> 3;
4454
4455
            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
4456 2
            $hasFrame = (0x00000080 & self::getInt2d($recordData, 28)) >> 7;
4457
4458
            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
4459 2
            $isUNC = (0x00000100 & self::getInt2d($recordData, 28)) >> 8;
4460
4461
            // offset within record data
4462 2
            $offset = 32;
4463
4464 2
            if ($hasDesc) {
4465
                // offset: 32; size: var; character count of description text
4466 1
                $dl = self::getInt4d($recordData, 32);
4467
                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
4468 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...
4469 1
                $offset += 4 + 2 * $dl;
4470
            }
4471 2
            if ($hasFrame) {
4472
                $fl = self::getInt4d($recordData, $offset);
4473
                $offset += 4 + 2 * $fl;
4474
            }
4475
4476
            // detect type of hyperlink (there are 4 types)
4477 2
            $hyperlinkType = null;
4478
4479 2
            if ($isUNC) {
4480
                $hyperlinkType = 'UNC';
4481 2
            } elseif (!$isFileLinkOrUrl) {
4482 2
                $hyperlinkType = 'workbook';
4483 2
            } elseif (ord($recordData{$offset}) == 0x03) {
4484
                $hyperlinkType = 'local';
4485 2
            } elseif (ord($recordData{$offset}) == 0xE0) {
4486 2
                $hyperlinkType = 'URL';
4487
            }
4488
4489
            switch ($hyperlinkType) {
4490 2
                case 'URL':
4491
                    // section 5.58.2: Hyperlink containing a URL
4492
                    // e.g. http://example.org/index.php
4493
4494
                    // offset: var; size: 16; GUID of URL Moniker
4495 2
                    $offset += 16;
4496
                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
4497 2
                    $us = self::getInt4d($recordData, $offset);
4498 2
                    $offset += 4;
4499
                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
4500 2
                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
4501 2
                    $nullOffset = strpos($url, 0x00);
4502 2
                    if ($nullOffset) {
4503 1
                        $url = substr($url, 0, $nullOffset);
4504
                    }
4505 2
                    $url .= $hasText ? '#' : '';
4506 2
                    $offset += $us;
4507 2
                    break;
4508 2
                case 'local':
4509
                    // section 5.58.3: Hyperlink to local file
4510
                    // examples:
4511
                    //   mydoc.txt
4512
                    //   ../../somedoc.xls#Sheet!A1
4513
4514
                    // offset: var; size: 16; GUI of File Moniker
4515
                    $offset += 16;
4516
4517
                    // offset: var; size: 2; directory up-level count.
4518
                    $upLevelCount = self::getInt2d($recordData, $offset);
4519
                    $offset += 2;
4520
4521
                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
4522
                    $sl = self::getInt4d($recordData, $offset);
4523
                    $offset += 4;
4524
4525
                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
4526
                    $shortenedFilePath = substr($recordData, $offset, $sl);
4527
                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
4528
                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
4529
4530
                    $offset += $sl;
4531
4532
                    // offset: var; size: 24; unknown sequence
4533
                    $offset += 24;
4534
4535
                    // extended file path
4536
                    // offset: var; size: 4; size of the following file link field including string lenth mark
4537
                    $sz = self::getInt4d($recordData, $offset);
4538
                    $offset += 4;
4539
4540
                    // only present if $sz > 0
4541
                    if ($sz > 0) {
4542
                        // offset: var; size: 4; size of the character array of the extended file path and name
4543
                        $xl = self::getInt4d($recordData, $offset);
4544
                        $offset += 4;
4545
4546
                        // offset: var; size 2; unknown
4547
                        $offset += 2;
4548
4549
                        // offset: var; size $xl; character array of the extended file path and name.
4550
                        $extendedFilePath = substr($recordData, $offset, $xl);
4551
                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
4552
                        $offset += $xl;
4553
                    }
4554
4555
                    // construct the path
4556
                    $url = str_repeat('..\\', $upLevelCount);
4557
                    $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available
0 ignored issues
show
Bug introduced by
The variable $extendedFilePath does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4558
                    $url .= $hasText ? '#' : '';
4559
4560
                    break;
4561 2
                case 'UNC':
4562
                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
4563
                    // todo: implement
4564
                    return;
4565 2
                case 'workbook':
4566
                    // section 5.58.5: Hyperlink to the Current Workbook
4567
                    // e.g. Sheet2!B1:C2, stored in text mark field
4568 2
                    $url = 'sheet://';
4569 2
                    break;
4570
                default:
4571
                    return;
4572
            }
4573
4574 2
            if ($hasText) {
4575
                // offset: var; size: 4; character count of text mark including trailing zero word
4576 2
                $tl = self::getInt4d($recordData, $offset);
4577 2
                $offset += 4;
4578
                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
4579 2
                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
4580 2
                $url .= $text;
4581
            }
4582
4583
            // apply the hyperlink to all the relevant cells
4584 2
            foreach (\PhpOffice\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
4585 2
                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
4586
            }
4587
        }
4588 2
    }
4589
4590
    /**
4591
     * Read DATAVALIDATIONS record
4592
     */
4593
    private function readDataValidations()
4594
    {
4595
        $length = self::getInt2d($this->data, $this->pos + 2);
4596
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
0 ignored issues
show
Unused Code introduced by
$recordData is not used, you could remove the assignment.

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

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

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

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

Loading history...
4597
4598
        // move stream pointer forward to next record
4599
        $this->pos += 4 + $length;
4600
    }
4601
4602
    /**
4603
     * Read DATAVALIDATION record
4604
     */
4605
    private function readDataValidation()
4606
    {
4607
        $length = self::getInt2d($this->data, $this->pos + 2);
4608
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4609
4610
        // move stream pointer forward to next record
4611
        $this->pos += 4 + $length;
4612
4613
        if ($this->readDataOnly) {
4614
            return;
4615
        }
4616
4617
        // offset: 0; size: 4; Options
4618
        $options = self::getInt4d($recordData, 0);
4619
4620
        // bit: 0-3; mask: 0x0000000F; type
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4621
        $type = (0x0000000F & $options) >> 0;
4622 View Code Duplication
        switch ($type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4623
            case 0x00:
4624
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_NONE;
4625
                break;
4626
            case 0x01:
4627
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE;
4628
                break;
4629
            case 0x02:
4630
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_DECIMAL;
4631
                break;
4632
            case 0x03:
4633
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST;
4634
                break;
4635
            case 0x04:
4636
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_DATE;
4637
                break;
4638
            case 0x05:
4639
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_TIME;
4640
                break;
4641
            case 0x06:
4642
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_TEXTLENGTH;
4643
                break;
4644
            case 0x07:
4645
                $type = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_CUSTOM;
4646
                break;
4647
        }
4648
4649
        // bit: 4-6; mask: 0x00000070; error type
4650
        $errorStyle = (0x00000070 & $options) >> 4;
4651 View Code Duplication
        switch ($errorStyle) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4652
            case 0x00:
4653
                $errorStyle = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_STOP;
4654
                break;
4655
            case 0x01:
4656
                $errorStyle = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_WARNING;
4657
                break;
4658
            case 0x02:
4659
                $errorStyle = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION;
4660
                break;
4661
        }
4662
4663
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
4664
        // I have only seen cases where this is 1
4665
        $explicitFormula = (0x00000080 & $options) >> 7;
0 ignored issues
show
Unused Code introduced by
$explicitFormula is not used, you could remove the assignment.

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

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

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

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

Loading history...
4666
4667
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
4668
        $allowBlank = (0x00000100 & $options) >> 8;
4669
4670
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
4671
        $suppressDropDown = (0x00000200 & $options) >> 9;
4672
4673
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
4674
        $showInputMessage = (0x00040000 & $options) >> 18;
4675
4676
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
4677
        $showErrorMessage = (0x00080000 & $options) >> 19;
4678
4679
        // bit: 20-23; mask: 0x00F00000; condition operator
4680
        $operator = (0x00F00000 & $options) >> 20;
4681 View Code Duplication
        switch ($operator) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4682
            case 0x00:
4683
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_BETWEEN;
4684
                break;
4685
            case 0x01:
4686
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTBETWEEN;
4687
                break;
4688
            case 0x02:
4689
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_EQUAL;
4690
                break;
4691
            case 0x03:
4692
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTEQUAL;
4693
                break;
4694
            case 0x04:
4695
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHAN;
4696
                break;
4697
            case 0x05:
4698
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHAN;
4699
                break;
4700
            case 0x06:
4701
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHANOREQUAL;
4702
                break;
4703
            case 0x07:
4704
                $operator = \PhpOffice\PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHANOREQUAL;
4705
                break;
4706
        }
4707
4708
        // offset: 4; size: var; title of the prompt box
4709
        $offset = 4;
4710
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4711
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4712
        $offset += $string['size'];
4713
4714
        // offset: var; size: var; title of the error box
4715
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4716
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4717
        $offset += $string['size'];
4718
4719
        // offset: var; size: var; text of the prompt box
4720
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4721
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
4722
        $offset += $string['size'];
4723
4724
        // offset: var; size: var; text of the error box
4725
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4726
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
4727
        $offset += $string['size'];
4728
4729
        // offset: var; size: 2; size of the formula data for the first condition
4730
        $sz1 = self::getInt2d($recordData, $offset);
4731
        $offset += 2;
4732
4733
        // offset: var; size: 2; not used
4734
        $offset += 2;
4735
4736
        // offset: var; size: $sz1; formula data for first condition (without size field)
4737
        $formula1 = substr($recordData, $offset, $sz1);
4738
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
4739
        try {
4740
            $formula1 = $this->getFormulaFromStructure($formula1);
4741
4742
            // in list type validity, null characters are used as item separators
4743
            if ($type == \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST) {
4744
                $formula1 = str_replace(chr(0), ',', $formula1);
4745
            }
4746
        } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4747
            return;
4748
        }
4749
        $offset += $sz1;
4750
4751
        // offset: var; size: 2; size of the formula data for the first condition
4752
        $sz2 = self::getInt2d($recordData, $offset);
4753
        $offset += 2;
4754
4755
        // offset: var; size: 2; not used
4756
        $offset += 2;
4757
4758
        // offset: var; size: $sz2; formula data for second condition (without size field)
4759
        $formula2 = substr($recordData, $offset, $sz2);
4760
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
4761
        try {
4762
            $formula2 = $this->getFormulaFromStructure($formula2);
4763
        } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
4764
            return;
4765
        }
4766
        $offset += $sz2;
4767
4768
        // offset: var; size: var; cell range address list with
4769
        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
4770
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
4771
4772
        foreach ($cellRangeAddresses as $cellRange) {
4773
            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4774
            foreach (\PhpOffice\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($stRange) as $coordinate) {
4775
                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4776
                $objValidation->setType($type);
4777
                $objValidation->setErrorStyle($errorStyle);
4778
                $objValidation->setAllowBlank((bool) $allowBlank);
4779
                $objValidation->setShowInputMessage((bool) $showInputMessage);
4780
                $objValidation->setShowErrorMessage((bool) $showErrorMessage);
4781
                $objValidation->setShowDropDown(!$suppressDropDown);
4782
                $objValidation->setOperator($operator);
4783
                $objValidation->setErrorTitle($errorTitle);
4784
                $objValidation->setError($error);
4785
                $objValidation->setPromptTitle($promptTitle);
4786
                $objValidation->setPrompt($prompt);
4787
                $objValidation->setFormula1($formula1);
4788
                $objValidation->setFormula2($formula2);
4789
            }
4790
        }
4791
    }
4792
4793
    /**
4794
     * Read SHEETLAYOUT record. Stores sheet tab color information.
4795
     */
4796 2
    private function readSheetLayout()
4797
    {
4798 2
        $length = self::getInt2d($this->data, $this->pos + 2);
4799 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4800
4801
        // move stream pointer to next record
4802 2
        $this->pos += 4 + $length;
4803
4804
        // local pointer in record data
4805 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...
4806
4807 2
        if (!$this->readDataOnly) {
4808
            // offset: 0; size: 2; repeated record identifier 0x0862
4809
4810
            // offset: 2; size: 10; not used
4811
4812
            // offset: 12; size: 4; size of record data
4813
            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
4814 2
            $sz = self::getInt4d($recordData, 12);
4815
4816
            switch ($sz) {
4817 2
                case 0x14:
4818
                    // offset: 16; size: 2; color index for sheet tab
4819 1
                    $colorIndex = self::getInt2d($recordData, 16);
4820 1
                    $color = Xls\Color::map($colorIndex, $this->palette, $this->version);
4821 1
                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
4822 1
                    break;
4823 1
                case 0x28:
4824
                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
4825 1
                    return;
4826
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

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

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

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

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

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

Loading history...
4997
4998
        // get spliced record data
4999
        $splicedRecordData = $this->getSplicedRecordData();
5000
        $recordData = $splicedRecordData['recordData'];
5001
5002
        // UNDER CONSTRUCTION
5003
5004
        // offset: 0; size: 2; image format
5005
        $cf = self::getInt2d($recordData, 0);
5006
5007
        // offset: 2; size: 2; environment from which the file was written
5008
        $env = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$env is not used, you could remove the assignment.

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

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

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

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

Loading history...
5009
5010
        // offset: 4; size: 4; length of the image data
5011
        $lcb = self::getInt4d($recordData, 4);
0 ignored issues
show
Unused Code introduced by
$lcb is not used, you could remove the assignment.

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

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

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

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

Loading history...
5012
5013
        // offset: 8; size: var; image data
5014
        $iData = substr($recordData, 8);
5015
5016
        switch ($cf) {
5017
            case 0x09: // Windows bitmap format
5018
                // BITMAPCOREINFO
5019
                // 1. BITMAPCOREHEADER
5020
                // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
5021
                $bcSize = self::getInt4d($iData, 0);
0 ignored issues
show
Unused Code introduced by
$bcSize is not used, you could remove the assignment.

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

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

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

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

Loading history...
5022
5023
                // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
5024
                $bcWidth = self::getInt2d($iData, 4);
5025
5026
                // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
5027
                $bcHeight = self::getInt2d($iData, 6);
5028
                $ih = imagecreatetruecolor($bcWidth, $bcHeight);
5029
5030
                // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
5031
5032
                // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
5033
                $bcBitCount = self::getInt2d($iData, 10);
0 ignored issues
show
Unused Code introduced by
$bcBitCount is not used, you could remove the assignment.

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

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

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

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

Loading history...
5034
5035
                $rgbString = substr($iData, 12);
5036
                $rgbTriples = [];
5037
                while (strlen($rgbString) > 0) {
5038
                    $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
5039
                    $rgbString = substr($rgbString, 3);
5040
                }
5041
                $x = 0;
5042
                $y = 0;
5043
                foreach ($rgbTriples as $i => $rgbTriple) {
5044
                    $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
5045
                    imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
5046
                    $x = ($x + 1) % $bcWidth;
5047
                    $y = $y + floor(($x + 1) / $bcWidth);
5048
                }
5049
                //imagepng($ih, 'image.png');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
5050
5051
                $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
5052
                $drawing->setPath($filename);
0 ignored issues
show
Bug introduced by
The variable $filename does not exist. Did you forget to declare it?

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

Loading history...
5053
                $drawing->setWorksheet($this->phpSheet);
5054
                break;
5055
            case 0x02: // Windows metafile or Macintosh PICT format
5056
            case 0x0e: // native format
5057
            default:
5058
                break;
5059
        }
5060
5061
        // getSplicedRecordData() takes care of moving current position in data stream
5062
    }
5063
5064
    /**
5065
     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
5066
     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
5067
     * In this case, we must treat the CONTINUE record as a MSODRAWING record
5068
     */
5069
    private function readContinue()
5070
    {
5071
        $length = self::getInt2d($this->data, $this->pos + 2);
5072
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5073
5074
        // check if we are reading drawing data
5075
        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
5076
        if ($this->drawingData == '') {
5077
            // move stream pointer to next record
5078
            $this->pos += 4 + $length;
5079
5080
            return;
5081
        }
5082
5083
        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
5084
        if ($length < 4) {
5085
            // move stream pointer to next record
5086
            $this->pos += 4 + $length;
5087
5088
            return;
5089
        }
5090
5091
        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
5092
        // look inside CONTINUE record to see if it looks like a part of an Escher stream
5093
        // we know that Escher stream may be split at least at
5094
        //        0xF003 MsofbtSpgrContainer
5095
        //        0xF004 MsofbtSpContainer
5096
        //        0xF00D MsofbtClientTextbox
5097
        $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
5098
5099
        $splitPoint = self::getInt2d($recordData, 2);
5100
        if (in_array($splitPoint, $validSplitPoints)) {
5101
            // get spliced record data (and move pointer to next record)
5102
            $splicedRecordData = $this->getSplicedRecordData();
5103
            $this->drawingData .= $splicedRecordData['recordData'];
5104
5105
            return;
5106
        }
5107
5108
        // move stream pointer to next record
5109
        $this->pos += 4 + $length;
5110
    }
5111
5112
    /**
5113
     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
5114
     * records are found. Splices the record data pieces and returns the combined string as if record data
5115
     * is in one piece.
5116
     * Moves to next current position in data stream to start of next record different from a CONtINUE record
5117
     *
5118
     * @return array
5119
     */
5120 4
    private function getSplicedRecordData()
5121
    {
5122 4
        $data = '';
5123 4
        $spliceOffsets = [];
5124
5125 4
        $i = 0;
5126 4
        $spliceOffsets[0] = 0;
5127
5128
        do {
5129 4
            ++$i;
5130
5131
            // offset: 0; size: 2; identifier
5132 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...
5133
            // offset: 2; size: 2; length
5134 4
            $length = self::getInt2d($this->data, $this->pos + 2);
5135 4
            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
5136
5137 4
            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
5138
5139 4
            $this->pos += 4 + $length;
5140 4
            $nextIdentifier = self::getInt2d($this->data, $this->pos);
5141 4
        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5142
5143
        $splicedData = [
5144 4
            'recordData' => $data,
5145 4
            'spliceOffsets' => $spliceOffsets,
5146
        ];
5147
5148 4
        return $splicedData;
5149
    }
5150
5151
    /**
5152
     * Convert formula structure into human readable Excel formula like 'A3+A5*5'
5153
     *
5154
     * @param string $formulaStructure The complete binary data for the formula
5155
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5156
     * @return string Human readable formula
5157
     */
5158 2
    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
5159
    {
5160
        // offset: 0; size: 2; size of the following formula data
5161 2
        $sz = self::getInt2d($formulaStructure, 0);
5162
5163
        // offset: 2; size: sz
5164 2
        $formulaData = substr($formulaStructure, 2, $sz);
5165
5166
        // offset: 2 + sz; size: variable (optional)
5167 2
        if (strlen($formulaStructure) > 2 + $sz) {
5168
            $additionalData = substr($formulaStructure, 2 + $sz);
5169
        } else {
5170 2
            $additionalData = '';
5171
        }
5172
5173 2
        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
5174
    }
5175
5176
    /**
5177
     * Take formula data and additional data for formula and return human readable formula
5178
     *
5179
     * @param string $formulaData The binary data for the formula itself
5180
     * @param string $additionalData Additional binary data going with the formula
5181
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5182
     * @return string Human readable formula
5183
     */
5184 2
    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
5185
    {
5186
        // start parsing the formula data
5187 2
        $tokens = [];
5188
5189 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...
5190 2
            $tokens[] = $token;
5191 2
            $formulaData = substr($formulaData, $token['size']);
5192
        }
5193
5194 2
        $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
5195
5196 2
        return $formulaString;
5197
    }
5198
5199
    /**
5200
     * Take array of tokens together with additional data for formula and return human readable formula
5201
     *
5202
     * @param array $tokens
5203
     * @param string $additionalData Additional binary data going with the formula
5204
     * @return string Human readable formula
5205
     */
5206 2
    private function createFormulaFromTokens($tokens, $additionalData)
5207
    {
5208
        // empty formula?
5209 2
        if (empty($tokens)) {
5210
            return '';
5211
        }
5212
5213 2
        $formulaStrings = [];
5214 2
        foreach ($tokens as $token) {
5215
            // initialize spaces
5216 2
            $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
5217 2
            $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
5218 2
            $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
5219 2
            $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
5220 2
            $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
5221 2
            $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
5222
5223 2
            switch ($token['name']) {
5224 2
                case 'tAdd': // addition
5225 2
                case 'tConcat': // addition
5226 2
                case 'tDiv': // division
5227 2
                case 'tEQ': // equality
5228 2
                case 'tGE': // greater than or equal
5229 2
                case 'tGT': // greater than
5230 2
                case 'tIsect': // intersection
5231 2
                case 'tLE': // less than or equal
5232 2
                case 'tList': // less than or equal
5233 2
                case 'tLT': // less than
5234 2
                case 'tMul': // multiplication
5235 2
                case 'tNE': // multiplication
5236 2
                case 'tPower': // power
5237 2
                case 'tRange': // range
5238 2
                case 'tSub': // subtraction
5239 2
                    $op2 = array_pop($formulaStrings);
5240 2
                    $op1 = array_pop($formulaStrings);
5241 2
                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
5242 2
                    unset($space0, $space1);
5243 2
                    break;
5244 2
                case 'tUplus': // unary plus
5245 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...
5246
                    $op = array_pop($formulaStrings);
5247
                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
5248
                    unset($space0, $space1);
5249
                    break;
5250 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...
5251
                    $op = array_pop($formulaStrings);
5252
                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
5253
                    unset($space0, $space1);
5254
                    break;
5255 2
                case 'tAttrVolatile': // indicates volatile function
5256 2
                case 'tAttrIf':
5257 2
                case 'tAttrSkip':
5258 2
                case 'tAttrChoose':
5259
                    // token is only important for Excel formula evaluator
5260
                    // do nothing
5261
                    break;
5262 2
                case 'tAttrSpace': // space / carriage return
5263
                    // space will be used when next token arrives, do not alter formulaString stack
5264
                    switch ($token['data']['spacetype']) {
5265
                        case 'type0':
5266
                            $space0 = str_repeat(' ', $token['data']['spacecount']);
5267
                            break;
5268
                        case 'type1':
5269
                            $space1 = str_repeat("\n", $token['data']['spacecount']);
5270
                            break;
5271
                        case 'type2':
5272
                            $space2 = str_repeat(' ', $token['data']['spacecount']);
5273
                            break;
5274
                        case 'type3':
5275
                            $space3 = str_repeat("\n", $token['data']['spacecount']);
5276
                            break;
5277
                        case 'type4':
5278
                            $space4 = str_repeat(' ', $token['data']['spacecount']);
5279
                            break;
5280
                        case 'type5':
5281
                            $space5 = str_repeat("\n", $token['data']['spacecount']);
5282
                            break;
5283
                    }
5284
                    break;
5285 2
                case 'tAttrSum': // SUM function with one parameter
5286 1
                    $op = array_pop($formulaStrings);
5287 1
                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
5288 1
                    unset($space0, $space1);
5289 1
                    break;
5290 2
                case 'tFunc': // function with fixed number of arguments
5291 2
                case 'tFuncV': // function with variable number of arguments
5292 1
                    if ($token['data']['function'] != '') {
5293
                        // normal function
5294 1
                        $ops = []; // array of operators
5295 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...
5296 1
                            $ops[] = array_pop($formulaStrings);
5297
                        }
5298 1
                        $ops = array_reverse($ops);
5299 1
                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
5300 1
                        unset($space0, $space1);
5301
                    } else {
5302
                        // add-in function
5303
                        $ops = []; // array of operators
5304 View Code Duplication
                        for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5305
                            $ops[] = array_pop($formulaStrings);
5306
                        }
5307
                        $ops = array_reverse($ops);
5308
                        $function = array_pop($formulaStrings);
5309
                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
5310
                        unset($space0, $space1);
5311
                    }
5312 1
                    break;
5313 2
                case 'tParen': // parenthesis
5314
                    $expression = array_pop($formulaStrings);
5315
                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
5316
                    unset($space2, $space3, $space4, $space5);
5317
                    break;
5318 2
                case 'tArray': // array constant
5319
                    $constantArray = self::readBIFF8ConstantArray($additionalData);
5320
                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
5321
                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
5322
                    unset($space0, $space1);
5323
                    break;
5324 2
                case 'tMemArea':
5325
                    // bite off chunk of additional data
5326
                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
5327
                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
5328
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5329
                    unset($space0, $space1);
5330
                    break;
5331 2
                case 'tArea': // cell range address
5332 2
                case 'tBool': // boolean
5333 2
                case 'tErr': // error code
5334 2
                case 'tInt': // integer
5335 2
                case 'tMemErr':
5336 2
                case 'tMemFunc':
5337 2
                case 'tMissArg':
5338 2
                case 'tName':
5339 2
                case 'tNameX':
5340 2
                case 'tNum': // number
5341 2
                case 'tRef': // single cell reference
5342 2
                case 'tRef3d': // 3d cell reference
5343 2
                case 'tArea3d': // 3d cell range reference
5344 1
                case 'tRefN':
5345 1
                case 'tAreaN':
5346 1
                case 'tStr': // string
5347 2
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5348 2
                    unset($space0, $space1);
5349 2
                    break;
5350
            }
5351
        }
5352 2
        $formulaString = $formulaStrings[0];
5353
5354 2
        return $formulaString;
5355
    }
5356
5357
    /**
5358
     * Fetch next token from binary formula data
5359
     *
5360
     * @param string $formulaData Formula data
5361
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5362
     * @throws Exception
5363
     * @return array
5364
     */
5365 2
    private function getNextToken($formulaData, $baseCell = 'A1')
5366
    {
5367
        // offset: 0; size: 1; token id
5368 2
        $id = ord($formulaData[0]); // token id
5369 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...
5370
5371
        switch ($id) {
5372 2
            case 0x03:
5373 1
                $name = 'tAdd';
5374 1
                $size = 1;
5375 1
                $data = '+';
5376 1
                break;
5377 2
            case 0x04:
5378
                $name = 'tSub';
5379
                $size = 1;
5380
                $data = '-';
5381
                break;
5382 2
            case 0x05:
5383 2
                $name = 'tMul';
5384 2
                $size = 1;
5385 2
                $data = '*';
5386 2
                break;
5387 2
            case 0x06:
5388
                $name = 'tDiv';
5389
                $size = 1;
5390
                $data = '/';
5391
                break;
5392 2
            case 0x07:
5393
                $name = 'tPower';
5394
                $size = 1;
5395
                $data = '^';
5396
                break;
5397 2
            case 0x08:
5398
                $name = 'tConcat';
5399
                $size = 1;
5400
                $data = '&';
5401
                break;
5402 2
            case 0x09:
5403
                $name = 'tLT';
5404
                $size = 1;
5405
                $data = '<';
5406
                break;
5407 2
            case 0x0A:
5408
                $name = 'tLE';
5409
                $size = 1;
5410
                $data = '<=';
5411
                break;
5412 2
            case 0x0B:
5413
                $name = 'tEQ';
5414
                $size = 1;
5415
                $data = '=';
5416
                break;
5417 2
            case 0x0C:
5418
                $name = 'tGE';
5419
                $size = 1;
5420
                $data = '>=';
5421
                break;
5422 2
            case 0x0D:
5423
                $name = 'tGT';
5424
                $size = 1;
5425
                $data = '>';
5426
                break;
5427 2
            case 0x0E:
5428 1
                $name = 'tNE';
5429 1
                $size = 1;
5430 1
                $data = '<>';
5431 1
                break;
5432 2
            case 0x0F:
5433
                $name = 'tIsect';
5434
                $size = 1;
5435
                $data = ' ';
5436
                break;
5437 2
            case 0x10:
5438
                $name = 'tList';
5439
                $size = 1;
5440
                $data = ',';
5441
                break;
5442 2
            case 0x11:
5443
                $name = 'tRange';
5444
                $size = 1;
5445
                $data = ':';
5446
                break;
5447 2
            case 0x12:
5448
                $name = 'tUplus';
5449
                $size = 1;
5450
                $data = '+';
5451
                break;
5452 2
            case 0x13:
5453
                $name = 'tUminus';
5454
                $size = 1;
5455
                $data = '-';
5456
                break;
5457 2
            case 0x14:
5458
                $name = 'tPercent';
5459
                $size = 1;
5460
                $data = '%';
5461
                break;
5462 2
            case 0x15:    //    parenthesis
5463
                $name = 'tParen';
5464
                $size = 1;
5465
                $data = null;
5466
                break;
5467 2
            case 0x16:    //    missing argument
5468
                $name = 'tMissArg';
5469
                $size = 1;
5470
                $data = '';
5471
                break;
5472 2
            case 0x17:    //    string
5473 1
                $name = 'tStr';
5474
                // offset: 1; size: var; Unicode string, 8-bit string length
5475 1
                $string = self::readUnicodeStringShort(substr($formulaData, 1));
5476 1
                $size = 1 + $string['size'];
5477 1
                $data = self::UTF8toExcelDoubleQuoted($string['value']);
5478 1
                break;
5479 2
            case 0x19:    //    Special attribute
5480
                // offset: 1; size: 1; attribute type flags:
5481 1
                switch (ord($formulaData[1])) {
5482 1
                    case 0x01:
5483
                        $name = 'tAttrVolatile';
5484
                        $size = 4;
5485
                        $data = null;
5486
                        break;
5487 1
                    case 0x02:
5488
                        $name = 'tAttrIf';
5489
                        $size = 4;
5490
                        $data = null;
5491
                        break;
5492 1
                    case 0x04:
5493
                        $name = 'tAttrChoose';
5494
                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
5495
                        $nc = self::getInt2d($formulaData, 2);
5496
                        // offset: 4; size: 2 * $nc
5497
                        // offset: 4 + 2 * $nc; size: 2
5498
                        $size = 2 * $nc + 6;
5499
                        $data = null;
5500
                        break;
5501 1
                    case 0x08:
5502
                        $name = 'tAttrSkip';
5503
                        $size = 4;
5504
                        $data = null;
5505
                        break;
5506 1
                    case 0x10:
5507 1
                        $name = 'tAttrSum';
5508 1
                        $size = 4;
5509 1
                        $data = null;
5510 1
                        break;
5511
                    case 0x40:
5512
                    case 0x41:
5513
                        $name = 'tAttrSpace';
5514
                        $size = 4;
5515
                        // offset: 2; size: 2; space type and position
5516
                        switch (ord($formulaData[2])) {
5517
                            case 0x00:
5518
                                $spacetype = 'type0';
5519
                                break;
5520
                            case 0x01:
5521
                                $spacetype = 'type1';
5522
                                break;
5523
                            case 0x02:
5524
                                $spacetype = 'type2';
5525
                                break;
5526
                            case 0x03:
5527
                                $spacetype = 'type3';
5528
                                break;
5529
                            case 0x04:
5530
                                $spacetype = 'type4';
5531
                                break;
5532
                            case 0x05:
5533
                                $spacetype = 'type5';
5534
                                break;
5535
                            default:
5536
                                throw new Exception('Unrecognized space type in tAttrSpace token');
5537
                                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

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

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

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

    return false;
}

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

Loading history...
5538
                        }
5539
                        // offset: 3; size: 1; number of inserted spaces/carriage returns
5540
                        $spacecount = ord($formulaData[3]);
5541
5542
                        $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
5543
                        break;
5544
                    default:
5545
                        throw new Exception('Unrecognized attribute flag in tAttr token');
5546
                        break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

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

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

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

    return false;
}

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

Loading history...
5547
                }
5548 1
                break;
5549 2
            case 0x1C:    //    error code
5550
                // offset: 1; size: 1; error code
5551
                $name = 'tErr';
5552
                $size = 2;
5553
                $data = Xls\ErrorCode::lookup(ord($formulaData[1]));
5554
                break;
5555 2
            case 0x1D:    //    boolean
5556
                // offset: 1; size: 1; 0 = false, 1 = true;
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
5557
                $name = 'tBool';
5558
                $size = 2;
5559
                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
5560
                break;
5561 2
            case 0x1E:    //    integer
5562
                // offset: 1; size: 2; unsigned 16-bit integer
5563
                $name = 'tInt';
5564
                $size = 3;
5565
                $data = self::getInt2d($formulaData, 1);
5566
                break;
5567 2
            case 0x1F:    //    number
5568
                // offset: 1; size: 8;
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

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

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

    return false;
}

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

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

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

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

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

    return false;
}

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

Loading history...
6512
                }
6513 1
                $data = ['function' => $function, 'args' => $args];
6514 1
                break;
6515 2
            case 0x23:    //    index to defined name
6516 2
            case 0x43:
6517 2
            case 0x63:
6518
                $name = 'tName';
6519
                $size = 5;
6520
                // offset: 1; size: 2; one-based index to definedname record
6521
                $definedNameIndex = self::getInt2d($formulaData, 1) - 1;
6522
                // offset: 2; size: 2; not used
6523
                $data = $this->definedname[$definedNameIndex]['name'];
6524
                break;
6525 2
            case 0x24:    //    single cell reference e.g. A5
6526 2
            case 0x44:
6527 2
            case 0x64:
6528 2
                $name = 'tRef';
6529 2
                $size = 5;
6530 2
                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
6531 2
                break;
6532 2
            case 0x25:    //    cell range reference to cells in the same sheet (2d)
6533 1
            case 0x45:
6534 1
            case 0x65:
6535 2
                $name = 'tArea';
6536 2
                $size = 9;
6537 2
                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
6538 2
                break;
6539 1
            case 0x26:    //    Constant reference sub-expression
6540 1
            case 0x46:
6541 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...
6542
                $name = 'tMemArea';
6543
                // offset: 1; size: 4; not used
6544
                // offset: 5; size: 2; size of the following subexpression
6545
                $subSize = self::getInt2d($formulaData, 5);
6546
                $size = 7 + $subSize;
6547
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6548
                break;
6549 1
            case 0x27:    //    Deleted constant reference sub-expression
6550 1
            case 0x47:
6551 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...
6552
                $name = 'tMemErr';
6553
                // offset: 1; size: 4; not used
6554
                // offset: 5; size: 2; size of the following subexpression
6555
                $subSize = self::getInt2d($formulaData, 5);
6556
                $size = 7 + $subSize;
6557
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6558
                break;
6559 1
            case 0x29:    //    Variable reference sub-expression
6560 1
            case 0x49:
6561 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...
6562
                $name = 'tMemFunc';
6563
                // offset: 1; size: 2; size of the following sub-expression
6564
                $subSize = self::getInt2d($formulaData, 1);
6565
                $size = 3 + $subSize;
6566
                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
6567
                break;
6568 1
            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
6569 1
            case 0x4C:
6570 1
            case 0x6C:
6571
                $name = 'tRefN';
6572
                $size = 5;
6573
                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
6574
                break;
6575 1
            case 0x2D:    //    Relative 2d range reference
6576 1
            case 0x4D:
6577 1
            case 0x6D:
6578
                $name = 'tAreaN';
6579
                $size = 9;
6580
                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
6581
                break;
6582 1
            case 0x39:    //    External name
6583 1
            case 0x59:
6584 1
            case 0x79:
6585
                $name = 'tNameX';
6586
                $size = 7;
6587
                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
6588
                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
6589
                $index = self::getInt2d($formulaData, 3);
6590
                // assume index is to EXTERNNAME record
6591
                $data = $this->externalNames[$index - 1]['name'];
6592
                // offset: 5; size: 2; not used
6593
                break;
6594 1
            case 0x3A:    //    3d reference to cell
6595 1
            case 0x5A:
6596 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...
6597
                $name = 'tRef3d';
6598
                $size = 7;
6599
6600
                try {
6601
                    // offset: 1; size: 2; index to REF entry
6602
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6603
                    // offset: 3; size: 4; cell address
6604
                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
6605
6606
                    $data = "$sheetRange!$cellAddress";
6607
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
6608
                    // deleted sheet reference
6609
                    $data = '#REF!';
6610
                }
6611
                break;
6612 1
            case 0x3B:    //    3d reference to cell range
6613
            case 0x5B:
6614 View Code Duplication
            case 0x7B:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6615 1
                $name = 'tArea3d';
6616 1
                $size = 11;
6617
6618
                try {
6619
                    // offset: 1; size: 2; index to REF entry
6620 1
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6621
                    // offset: 3; size: 8; cell address
6622 1
                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
6623
6624 1
                    $data = "$sheetRange!$cellRangeAddress";
6625
                } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
6626
                    // deleted sheet reference
6627
                    $data = '#REF!';
6628
                }
6629 1
                break;
6630
            // Unknown cases    // don't know how to deal with
6631
            default:
6632
                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
6633
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

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

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

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

    return false;
}

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

Loading history...
6634
        }
6635
6636
        return [
6637 2
            'id' => $id,
6638 2
            'name' => $name,
6639 2
            'size' => $size,
6640 2
            'data' => $data,
6641
        ];
6642
    }
6643
6644
    /**
6645
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
6646
     * section 3.3.4
6647
     *
6648
     * @param string $cellAddressStructure
6649
     * @return string
6650
     */
6651 2
    private function readBIFF8CellAddress($cellAddressStructure)
6652
    {
6653
        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
6654 2
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6655
6656
        // offset: 2; size: 2; index to column or column offset + relative flags
6657
        // bit: 7-0; mask 0x00FF; column index
6658 2
        $column = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($cellAddressStructure, 2));
6659
6660
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6661 2
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
6662 1
            $column = '$' . $column;
6663
        }
6664
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6665 2
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6666 1
            $row = '$' . $row;
6667
        }
6668
6669 2
        return $column . $row;
6670
    }
6671
6672
    /**
6673
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6674
     * to indicate offsets from a base cell
6675
     * section 3.3.4
6676
     *
6677
     * @param string $cellAddressStructure
6678
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
6679
     * @return string
6680
     */
6681
    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
6682
    {
6683
        list($baseCol, $baseRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6684
        $baseCol = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6685
6686
        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
6687
        $rowIndex = self::getInt2d($cellAddressStructure, 0);
6688
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6689
6690
        // offset: 2; size: 2; index to column or column offset + relative flags
6691
        // bit: 7-0; mask 0x00FF; column index
6692
        $colIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
6693
6694
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6695 View Code Duplication
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6696
            $column = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colIndex);
6697
            $column = '$' . $column;
6698
        } else {
6699
            $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
6700
            $column = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $colIndex);
6701
        }
6702
6703
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6704
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6705
            $row = '$' . $row;
6706
        } else {
6707
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
6708
            $row = $baseRow + $rowIndex;
6709
        }
6710
6711
        return $column . $row;
6712
    }
6713
6714
    /**
6715
     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
6716
     * always fixed range
6717
     * section 2.5.14
6718
     *
6719
     * @param string $subData
6720
     * @throws Exception
6721
     * @return string
6722
     */
6723 4
    private function readBIFF5CellRangeAddressFixed($subData)
6724
    {
6725
        // offset: 0; size: 2; index to first row
6726 4
        $fr = self::getInt2d($subData, 0) + 1;
6727
6728
        // offset: 2; size: 2; index to last row
6729 4
        $lr = self::getInt2d($subData, 2) + 1;
6730
6731
        // offset: 4; size: 1; index to first column
6732 4
        $fc = ord($subData{4});
6733
6734
        // offset: 5; size: 1; index to last column
6735 4
        $lc = ord($subData{5});
6736
6737
        // check values
6738 4
        if ($fr > $lr || $fc > $lc) {
6739
            throw new Exception('Not a cell range address');
6740
        }
6741
6742
        // column index to letter
6743 4
        $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6744 4
        $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6745
6746 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...
6747 4
            return "$fc$fr";
6748
        }
6749
6750 1
        return "$fc$fr:$lc$lr";
6751
    }
6752
6753
    /**
6754
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
6755
     * always fixed range
6756
     * section 2.5.14
6757
     *
6758
     * @param string $subData
6759
     * @throws Exception
6760
     * @return string
6761
     */
6762 2
    private function readBIFF8CellRangeAddressFixed($subData)
6763
    {
6764
        // offset: 0; size: 2; index to first row
6765 2
        $fr = self::getInt2d($subData, 0) + 1;
6766
6767
        // offset: 2; size: 2; index to last row
6768 2
        $lr = self::getInt2d($subData, 2) + 1;
6769
6770
        // offset: 4; size: 2; index to first column
6771 2
        $fc = self::getInt2d($subData, 4);
6772
6773
        // offset: 6; size: 2; index to last column
6774 2
        $lc = self::getInt2d($subData, 6);
6775
6776
        // check values
6777 2
        if ($fr > $lr || $fc > $lc) {
6778
            throw new Exception('Not a cell range address');
6779
        }
6780
6781
        // column index to letter
6782 2
        $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6783 2
        $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6784
6785 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...
6786 2
            return "$fc$fr";
6787
        }
6788
6789 2
        return "$fc$fr:$lc$lr";
6790
    }
6791
6792
    /**
6793
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
6794
     * there are flags indicating whether column/row index is relative
6795
     * section 3.3.4
6796
     *
6797
     * @param string $subData
6798
     * @return string
6799
     */
6800 2
    private function readBIFF8CellRangeAddress($subData)
6801
    {
6802
        // todo: if cell range is just a single cell, should this funciton
6803
        // not just return e.g. 'A1' and not 'A1:A1' ?
6804
6805
        // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
6806 2
            $fr = self::getInt2d($subData, 0) + 1;
6807
6808
        // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
6809 2
            $lr = self::getInt2d($subData, 2) + 1;
6810
6811
        // offset: 4; size: 2; index to first column or column offset + relative flags
6812
6813
        // bit: 7-0; mask 0x00FF; column index
6814 2
        $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 4));
6815
6816
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6817 2
        if (!(0x4000 & self::getInt2d($subData, 4))) {
6818 1
            $fc = '$' . $fc;
6819
        }
6820
6821
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6822 2
        if (!(0x8000 & self::getInt2d($subData, 4))) {
6823 1
            $fr = '$' . $fr;
6824
        }
6825
6826
        // offset: 6; size: 2; index to last column or column offset + relative flags
6827
6828
        // bit: 7-0; mask 0x00FF; column index
6829 2
        $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 6));
6830
6831
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6832 2
        if (!(0x4000 & self::getInt2d($subData, 6))) {
6833 1
            $lc = '$' . $lc;
6834
        }
6835
6836
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6837 2
        if (!(0x8000 & self::getInt2d($subData, 6))) {
6838 1
            $lr = '$' . $lr;
6839
        }
6840
6841 2
        return "$fc$fr:$lc$lr";
6842
    }
6843
6844
    /**
6845
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6846
     * to indicate offsets from a base cell
6847
     * section 3.3.4
6848
     *
6849
     * @param string $subData
6850
     * @param string $baseCell Base cell
6851
     * @return string Cell range address
6852
     */
6853
    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
6854
    {
6855
        list($baseCol, $baseRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6856
        $baseCol = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6857
6858
        // TODO: if cell range is just a single cell, should this funciton
6859
        // not just return e.g. 'A1' and not 'A1:A1' ?
6860
6861
        // offset: 0; size: 2; first row
6862
        $frIndex = self::getInt2d($subData, 0); // adjust below
6863
6864
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
6865
        $lrIndex = self::getInt2d($subData, 2); // adjust below
6866
6867
        // offset: 4; size: 2; first column with relative/absolute flags
6868
6869
        // bit: 7-0; mask 0x00FF; column index
6870
        $fcIndex = 0x00FF & self::getInt2d($subData, 4);
6871
6872
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6873 View Code Duplication
        if (!(0x4000 & self::getInt2d($subData, 4))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6874
            // absolute column index
6875
            $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($fcIndex);
6876
            $fc = '$' . $fc;
6877
        } else {
6878
            // column offset
6879
            $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
6880
            $fc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $fcIndex);
6881
        }
6882
6883
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6884 View Code Duplication
        if (!(0x8000 & self::getInt2d($subData, 4))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6885
            // absolute row index
6886
            $fr = $frIndex + 1;
6887
            $fr = '$' . $fr;
6888
        } else {
6889
            // row offset
6890
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
6891
            $fr = $baseRow + $frIndex;
6892
        }
6893
6894
        // offset: 6; size: 2; last column with relative/absolute flags
6895
6896
        // bit: 7-0; mask 0x00FF; column index
6897
        $lcIndex = 0x00FF & self::getInt2d($subData, 6);
6898
        $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6899
        $lc = \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...
6900
6901
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6902 View Code Duplication
        if (!(0x4000 & self::getInt2d($subData, 6))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6903
            // absolute column index
6904
            $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lcIndex);
6905
            $lc = '$' . $lc;
6906
        } else {
6907
            // column offset
6908
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6909
            $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
6910
        }
6911
6912
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6913 View Code Duplication
        if (!(0x8000 & self::getInt2d($subData, 6))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6914
            // absolute row index
6915
            $lr = $lrIndex + 1;
6916
            $lr = '$' . $lr;
6917
        } else {
6918
            // row offset
6919
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
6920
            $lr = $baseRow + $lrIndex;
6921
        }
6922
6923
        return "$fc$fr:$lc$lr";
6924
    }
6925
6926
    /**
6927
     * Read BIFF8 cell range address list
6928
     * section 2.5.15
6929
     *
6930
     * @param string $subData
6931
     * @return array
6932
     */
6933 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...
6934
    {
6935 2
        $cellRangeAddresses = [];
6936
6937
        // offset: 0; size: 2; number of the following cell range addresses
6938 2
        $nm = self::getInt2d($subData, 0);
6939
6940 2
        $offset = 2;
6941
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
6942 2
        for ($i = 0; $i < $nm; ++$i) {
6943 2
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
6944 2
            $offset += 8;
6945
        }
6946
6947
        return [
6948 2
            'size' => 2 + 8 * $nm,
6949 2
            'cellRangeAddresses' => $cellRangeAddresses,
6950
        ];
6951
    }
6952
6953
    /**
6954
     * Read BIFF5 cell range address list
6955
     * section 2.5.15
6956
     *
6957
     * @param string $subData
6958
     * @return array
6959
     */
6960 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...
6961
    {
6962 4
        $cellRangeAddresses = [];
6963
6964
        // offset: 0; size: 2; number of the following cell range addresses
6965 4
        $nm = self::getInt2d($subData, 0);
6966
6967 4
        $offset = 2;
6968
        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
6969 4
        for ($i = 0; $i < $nm; ++$i) {
6970 4
            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
6971 4
            $offset += 6;
6972
        }
6973
6974
        return [
6975 4
            'size' => 2 + 6 * $nm,
6976 4
            'cellRangeAddresses' => $cellRangeAddresses,
6977
        ];
6978
    }
6979
6980
    /**
6981
     * Get a sheet range like Sheet1:Sheet3 from REF index
6982
     * Note: If there is only one sheet in the range, one gets e.g Sheet1
6983
     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
6984
     * in which case an Exception is thrown
6985
     *
6986
     * @param int $index
6987
     * @throws Exception
6988
     * @return string|false
6989
     */
6990 1
    private function readSheetRangeByRefIndex($index)
6991
    {
6992 1
        if (isset($this->ref[$index])) {
6993 1
            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
6994
6995
            switch ($type) {
6996 1
                case 'internal':
6997
                    // check if we have a deleted 3d reference
6998 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...
6999
                        throw new Exception('Deleted sheet reference');
7000
                    }
7001
7002
                    // we have normal sheet range (collapsed or uncollapsed)
7003 1
                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
7004 1
                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
7005
7006 1
                    if ($firstSheetName == $lastSheetName) {
7007
                        // collapsed sheet range
7008 1
                        $sheetRange = $firstSheetName;
7009
                    } else {
7010
                        $sheetRange = "$firstSheetName:$lastSheetName";
7011
                    }
7012
7013
                    // escape the single-quotes
7014 1
                    $sheetRange = str_replace("'", "''", $sheetRange);
7015
7016
                    // if there are special characters, we need to enclose the range in single-quotes
7017
                    // todo: check if we have identified the whole set of special characters
7018
                    // it seems that the following characters are not accepted for sheet names
7019
                    // and we may assume that they are not present: []*/:\?
7020 1
                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
7021
                        $sheetRange = "'$sheetRange'";
7022
                    }
7023
7024 1
                    return $sheetRange;
7025
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

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

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

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

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

    return false;
}

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

Loading history...
7030
            }
7031
        }
7032
7033
        return false;
7034
    }
7035
7036
    /**
7037
     * read BIFF8 constant value array from array data
7038
     * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
7039
     * section 2.5.8
7040
     *
7041
     * @param string $arrayData
7042
     * @return array
7043
     */
7044
    private static function readBIFF8ConstantArray($arrayData)
7045
    {
7046
        // offset: 0; size: 1; number of columns decreased by 1
7047
        $nc = ord($arrayData[0]);
7048
7049
        // offset: 1; size: 2; number of rows decreased by 1
7050
        $nr = self::getInt2d($arrayData, 1);
7051
        $size = 3; // initialize
7052
        $arrayData = substr($arrayData, 3);
7053
7054
        // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
7055
        $matrixChunks = [];
7056
        for ($r = 1; $r <= $nr + 1; ++$r) {
7057
            $items = [];
7058
            for ($c = 1; $c <= $nc + 1; ++$c) {
7059
                $constant = self::readBIFF8Constant($arrayData);
7060
                $items[] = $constant['value'];
7061
                $arrayData = substr($arrayData, $constant['size']);
7062
                $size += $constant['size'];
7063
            }
7064
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
7065
        }
7066
        $matrix = '{' . implode(';', $matrixChunks) . '}';
7067
7068
        return [
7069
            'value' => $matrix,
7070
            'size' => $size,
7071
        ];
7072
    }
7073
7074
    /**
7075
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
7076
     * section 2.5.7
7077
     * returns e.g. array('value' => '5', 'size' => 9)
7078
     *
7079
     * @param string $valueData
7080
     * @return array
7081
     */
7082
    private static function readBIFF8Constant($valueData)
7083
    {
7084
        // offset: 0; size: 1; identifier for type of constant
7085
        $identifier = ord($valueData[0]);
7086
7087
        switch ($identifier) {
7088
            case 0x00: // empty constant (what is this?)
7089
                $value = '';
7090
                $size = 9;
7091
                break;
7092
            case 0x01: // number
7093
                // offset: 1; size: 8; IEEE 754 floating-point value
7094
                $value = self::extractNumber(substr($valueData, 1, 8));
7095
                $size = 9;
7096
                break;
7097
            case 0x02: // string value
7098
                // offset: 1; size: var; Unicode string, 16-bit string length
7099
                $string = self::readUnicodeStringLong(substr($valueData, 1));
7100
                $value = '"' . $string['value'] . '"';
7101
                $size = 1 + $string['size'];
7102
                break;
7103
            case 0x04: // boolean
7104
                // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
7105
                if (ord($valueData[1])) {
7106
                    $value = 'TRUE';
7107
                } else {
7108
                    $value = 'FALSE';
7109
                }
7110
                $size = 9;
7111
                break;
7112
            case 0x10: // error code
7113
                // offset: 1; size: 1; error code
7114
                $value = Xls\ErrorCode::lookup(ord($valueData[1]));
7115
                $size = 9;
7116
                break;
7117
        }
7118
7119
        return [
7120
            'value' => $value,
0 ignored issues
show
Bug introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
7122
        ];
7123
    }
7124
7125
    /**
7126
     * Extract RGB color
7127
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
7128
     *
7129
     * @param string $rgb Encoded RGB value (4 bytes)
7130
     * @return array
7131
     */
7132 2
    private static function readRGB($rgb)
7133
    {
7134
        // offset: 0; size 1; Red component
7135 2
        $r = ord($rgb{0});
7136
7137
        // offset: 1; size: 1; Green component
7138 2
        $g = ord($rgb{1});
7139
7140
        // offset: 2; size: 1; Blue component
7141 2
        $b = ord($rgb{2});
7142
7143
        // HEX notation, e.g. 'FF00FC'
7144 2
        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
7145
7146 2
        return ['rgb' => $rgb];
7147
    }
7148
7149
    /**
7150
     * Read byte string (8-bit string length)
7151
     * OpenOffice documentation: 2.5.2
7152
     *
7153
     * @param string $subData
7154
     * @return array
7155
     */
7156 View Code Duplication
    private function readByteStringShort($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
7157
    {
7158
        // offset: 0; size: 1; length of the string (character count)
7159
        $ln = ord($subData[0]);
7160
7161
        // offset: 1: size: var; character array (8-bit characters)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
7162
        $value = $this->decodeCodepage(substr($subData, 1, $ln));
7163
7164
        return [
7165
            'value' => $value,
7166
            'size' => 1 + $ln, // size in bytes of data structure
7167
        ];
7168
    }
7169
7170
    /**
7171
     * Read byte string (16-bit string length)
7172
     * OpenOffice documentation: 2.5.2
7173
     *
7174
     * @param string $subData
7175
     * @return array
7176
     */
7177 View Code Duplication
    private function readByteStringLong($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
7178
    {
7179
        // offset: 0; size: 2; length of the string (character count)
7180
        $ln = self::getInt2d($subData, 0);
7181
7182
        // offset: 2: size: var; character array (8-bit characters)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
7183
        $value = $this->decodeCodepage(substr($subData, 2));
7184
7185
        //return $string;
7186
        return [
7187
            'value' => $value,
7188
            'size' => 2 + $ln, // size in bytes of data structure
7189
        ];
7190
    }
7191
7192
    /**
7193
     * Extracts an Excel Unicode short string (8-bit string length)
7194
     * OpenOffice documentation: 2.5.3
7195
     * function will automatically find out where the Unicode string ends.
7196
     *
7197
     * @param string $subData
7198
     * @return array
7199
     */
7200 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...
7201
    {
7202 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...
7203
7204
        // offset: 0: size: 1; length of the string (character count)
7205 4
        $characterCount = ord($subData[0]);
7206
7207 4
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7208
7209
        // add 1 for the string length
7210 4
        $string['size'] += 1;
7211
7212 4
        return $string;
7213
    }
7214
7215
    /**
7216
     * Extracts an Excel Unicode long string (16-bit string length)
7217
     * OpenOffice documentation: 2.5.3
7218
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7219
     *
7220
     * @param string $subData
7221
     * @return array
7222
     */
7223 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...
7224
    {
7225 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...
7226
7227
        // offset: 0: size: 2; length of the string (character count)
7228 4
        $characterCount = self::getInt2d($subData, 0);
7229
7230 4
        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7231
7232
        // add 2 for the string length
7233 4
        $string['size'] += 2;
7234
7235 4
        return $string;
7236
    }
7237
7238
    /**
7239
     * Read Unicode string with no string length field, but with known character count
7240
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7241
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
7242
     *
7243
     * @param string $subData
7244
     * @param int $characterCount
7245
     * @return array
7246
     */
7247 4
    private static function readUnicodeString($subData, $characterCount)
7248
    {
7249 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...
7250
7251
        // offset: 0: size: 1; option flags
7252
        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
7253 4
        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7254
7255
        // bit: 2; mask: 0x04; Asian phonetic settings
7256 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...
7257
7258
        // bit: 3; mask: 0x08; Rich-Text settings
7259 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...
7260
7261
        // offset: 1: size: var; character array
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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